| Tenouk C & C++ | MFC Home | Automation 4 | Automation 6 | Download | Site Index |


 

 

 

 

 

 

Automation 5

 

 

 

 

 

 

 

Program examples compiled using Visual C++ 6.0 compiler on Windows XP Pro machine with Service Pack 2. The Excel version is Excel 2003/Office 11. Topics and sub topics for this tutorial are listed below. Don’t forget to read Tenouk’s small disclaimer. The supplementary notes for this tutorial are Demo.xls, mymfc29A.xls, mymfc29B.xls, automation., variant and Colevariant class.

  1. The Story

  2. Debugging a DLL Component

 

 

The Story

 

The MYMFC29B program is fairly simple. An Automation component class, identified by the registered name Mymfc29B.Auto, has the following properties and method.

 

Method/property

Description

LongData

Long integer property.

TextData

VARIANT property.

DisplayDialog

Method - no parameters, BOOL return.

 

Table 4.

 

DisplayDialog displays the MYMFC29B data gathering dialog box shown in Figure 66. An Excel macro passes two cell values to the DLL and then updates the same cells with the updated values.

 

Figure 66: The MYMFC29B DLL dialog in action.

 

Figure 66: The MYMFC29B DLL dialog in action.

 

The example was first generated as an MFC AppWizard DLL with the Regular DLL Using Shared MFC DLL option and the Automation option selected.

 

 

Debugging a DLL Component

 

To debug a DLL, you must tell the debugger which EXE file to load. Choose Settings from Visual C++'s Project menu, and then enter the controller's full pathname (including the EXE extension) in the Executable For Debug Session box on the Debug page.

 

Figure 67: Changing the Visual C++ project Debug setting.

 

Figure 67: Changing the Visual C++ project Debug setting.

 

When you click the Go button on the Debug toolbar, your controller will start (loading the DLL as part of its process) and then wait for you to activate the component. When you activate the component, your DLL in the debugger should then construct its component object. It might be a good idea to include a TRACE statement in the component object's constructor. Don't forget that your DLL must be registered before the client can load it. Here's another option. If you have the source code for the client program, you can start the client program in the debugger. When the client loads the component DLL, you can see the output from the component program's TRACE statements.

 

Here are the steps for building and testing the MYMFC29B component DLL. From Visual C++, open the \mymfc29B.dsw workspace. Build the project. Register the DLL with the regsvr32 utility as shown below.

 

Figure 68: Registering mymfc29B.dll using regsvr32 at command prompt.

 

Figure 68: Registering mymfc29B.dll using regsvr32 at command prompt.

 

Start Excel, and then open the mymfc29B.xls workbook file.

Click the Load DLL button, and then click the Gather Data button and dialog as shown in Figure 70 is launched. Edit the String and Long data, click the OK, and watch the new values appear in the spreadsheet.

 

Figure 69: Excel as client to test MYMFC29B component.

 

Figure 69: Excel as client to test MYMFC29B component.

 

Figure 70: The MYMFC29B DLL dialog in action.

 

Figure 70: The MYMFC29B DLL dialog in action.

 

Click the Unload DLL button. If you've started the DLL (and Excel) from the debugger, you can watch the Debug window to be sure the DLL's ExitInstance() function is called.

 

Now let's look at the MYMFC29B code. Like an MFC EXE, an MFC regular DLL has an application class (derived from CWinApp) and a global application object. The overridden InitInstance() member function in mymfc29B.cpp looks like this:

BOOL CMymfc29BApp::InitInstance()

{

    TRACE("CMymfc29BApp::InitInstance\n");

    // Register all OLE server (factories) as running.  This

    //  enables the OLE libraries to create objects from other applications.

    COleObjectFactory::RegisterAll();

 

    return TRUE;

}

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

MFC and Automation program example - C++ code snippet

 

Listing 17.

 

There's also an ExitInstance() function for diagnostic purposes only, as well as the following code for the three standard COM DLL exported functions:

STDAPI DllGetClassObject(REFCLSID rclsid, REFIID riid, LPVOID* ppv)

{

    AFX_MANAGE_STATE(AfxGetStaticModuleState());

    return AfxDllGetClassObject(rclsid, riid, ppv);

}

 

STDAPI DllCanUnloadNow(void)

{

    AFX_MANAGE_STATE(AfxGetStaticModuleState());

    return AfxDllCanUnloadNow();

}

 

STDAPI DllRegisterServer(void)

{

    AFX_MANAGE_STATE(AfxGetStaticModuleState());

    COleObjectFactory::UpdateRegistryAll();

    // VERIFY(AfxOleRegisterTypeLib(AfxGetInstanceHandle(), theTypeLibGUID,  "mymfc29B.tlb"));

       return S_OK;

}

 

MFC and Automation program example - C++ code snippet

 

Listing 18.

 

The PromptDl.cpp file contains code for the CPromptDlg class, but that class is a standard class derived from CDialog. The file PromptDl.h contains the CPromptDlg class header.

 

The CMymfc29BAuto class, the Automation component class initially generated by ClassWizard (with the Createable By Type ID option), is more interesting. This class is exposed to COM under the program ID mymfc29B.Auto. Listing 19 below shows the header file mymfc29BAuto.h.

 

 

 

 

 

 

 

 

 

 

 

 

 

MYMFC29BAUTO.H

 

#if !defined(AFX_MYMFC29BAUTO_H__DE94AEB7_260A_4D9D_BB2C_7023D4D6BBCA__INCLUDED_)

#define AFX_MYMFC29BAUTO_H__DE94AEB7_260A_4D9D_BB2C_7023D4D6BBCA__INCLUDED_

 

#if _MSC_VER > 1000

#pragma once

#endif // _MSC_VER > 1000

// Mymfc29BAuto.h : header file

//

 

/////////////////////////////////////////////////////////////////////////////

// CMymfc29BAuto command target

 

class CMymfc29BAuto : public CCmdTarget

{

       DECLARE_DYNCREATE(CMymfc29BAuto)

       CMymfc29BAuto();           // protected constructor used by dynamic creation

 

// Attributes

public:

 

// Operations

public:

 

// Overrides

       // ClassWizard generated virtual function overrides

       //{{AFX_VIRTUAL(CMymfc29BAuto)

       public:

       virtual void OnFinalRelease();

       //}}AFX_VIRTUAL

 

// Implementation

protected:

       virtual ~CMymfc29BAuto();

 

       // Generated message map functions

       //{{AFX_MSG(CMymfc29BAuto)

              // NOTE - the ClassWizard will add and remove member functions here.

       //}}AFX_MSG

 

       DECLARE_MESSAGE_MAP()

       DECLARE_OLECREATE(CMymfc29BAuto)

 

       // Generated OLE dispatch map functions

       //{{AFX_DISPATCH(CMymfc29BAuto)

       long m_lData;

       afx_msg void OnLongDataChanged();

       VARIANT m_vaTextData;

       afx_msg void OnTextDataChanged();

       afx_msg BOOL DisplayDialog();

       //}}AFX_DISPATCH

       DECLARE_DISPATCH_MAP()

       DECLARE_INTERFACE_MAP()

};

 

/////////////////////////////////////////////////////////////////////////////

 

//{{AFX_INSERT_LOCATION}}

// Microsoft Visual C++ will insert additional declarations immediately before the previous line.

 

#endif // !defined(AFX_MYMFC29BAUTO_H__DE94AEB7_260A_4D9D_BB2C_7023D4D6BBCA__INCLUDED_)

 

 

Listing 19:  Excerpt from the mymfc29BAuto.h header file.

 

 

Listing 20 shows the implementation file mymfc29BAuto.cpp.

 

 

 

 

 

 

 

 

 

 

 

 

MYMFC29BAUTO.CPP

 

// Mymfc29BAuto.cpp : implementation file

//

 

#include "stdafx.h"

#include "mymfc29B.h"

#include "Mymfc29BAuto.h"

#include "PromptDl.h"

 

#ifdef _DEBUG

#define new DEBUG_NEW

#undef THIS_FILE

static char THIS_FILE[] = __FILE__;

#endif

 

/////////////////////////////////////////////////////////////////////////////

// CMymfc29BAuto

 

IMPLEMENT_DYNCREATE(CMymfc29BAuto, CCmdTarget)

 

CMymfc29BAuto::CMymfc29BAuto()

{

       EnableAutomation();

       // To keep the application running as long as an OLE automation

       //     object is active, the constructor calls AfxOleLockApp.

       ::VariantInit(&m_vaTextData); // necessary initialization

       m_lData = 0;

       AfxOleLockApp();

}

 

CMymfc29BAuto::~CMymfc29BAuto()

{

       // To terminate the application when all objects created with

       //     with OLE automation, the destructor calls AfxOleUnlockApp.

       AfxOleUnlockApp();

}

 

void CMymfc29BAuto::OnFinalRelease()

{

       // When the last reference for an automation object is released

       // OnFinalRelease is called.  The base class will automatically

       // deletes the object.  Add additional cleanup required for your object before calling the base class.

       CCmdTarget::OnFinalRelease();

}

 

BEGIN_MESSAGE_MAP(CMymfc29BAuto, CCmdTarget)

       //{{AFX_MSG_MAP(CMymfc29BAuto)

              // NOTE - the ClassWizard will add and remove mapping macros here.

       //}}AFX_MSG_MAP

END_MESSAGE_MAP()

 

BEGIN_DISPATCH_MAP(CMymfc29BAuto, CCmdTarget)

       //{{AFX_DISPATCH_MAP(CMymfc29BAuto)

       DISP_PROPERTY_NOTIFY(CMymfc29BAuto, "LongData", m_lData, OnLongDataChanged, VT_I4)

       DISP_PROPERTY_NOTIFY(CMymfc29BAuto, "TextData", m_vaTextData, OnTextDataChanged, VT_VARIANT)

       DISP_FUNCTION(CMymfc29BAuto, "DisplayDialog", DisplayDialog, VT_BOOL, VTS_NONE)

       //}}AFX_DISPATCH_MAP

END_DISPATCH_MAP()

 

// Note: we add support for IID_IMymfc29BAuto to support type safe binding

//  from VBA.  This IID must match the GUID that is attached to the dispinterface in the .ODL file.

 

// {7A97BA38-BF4A-4586-93C6-72B5EE7E0DC2}

static const IID IID_IMymfc29BAuto = { 0x7a97ba38, 0xbf4a, 0x4586, { 0x93, 0xc6, 0x72, 0xb5, 0xee, 0x7e, 0xd, 0xc2 } };

 

BEGIN_INTERFACE_MAP(CMymfc29BAuto, CCmdTarget)

       INTERFACE_PART(CMymfc29BAuto, IID_IMymfc29BAuto, Dispatch)

END_INTERFACE_MAP()

 

// {39D9E31F-25CB-4511-B5A8-5406E29BA565}

IMPLEMENT_OLECREATE(CMymfc29BAuto, "mymfc29B.Auto", 0x39d9e31f, 0x25cb, 0x4511, 0xb5, 0xa8, 0x54, 0x6, 0xe2, 0x9b, 0xa5, 0x65)

 

/////////////////////////////////////////////////////////////////////////////

// CMymfc29BAuto message handlers

 

void CMymfc29BAuto::OnLongDataChanged()

{

       // TODO: Add notification handler code

       TRACE("CMymfc29BAuto::OnLongDataChanged\n");

}

 

void CMymfc29BAuto::OnTextDataChanged()

{

       // TODO: Add notification handler code

       TRACE("CMymfc29BAuto::OnTextDataChanged\n");

}

 

BOOL CMymfc29BAuto::DisplayDialog()

{

    // TODO: Add your dispatch handler code here

    TRACE("Entering CMymfc29BAuto::DisplayDialog %p\n", this);

    BOOL bRet = TRUE;

    AfxLockTempMaps();  // See MFC Tech Note #3

    CWnd* pTopWnd = CWnd::FromHandle(::GetTopWindow(NULL));

    try

       {

        CPromptDlg dlg /*(pTopWnd)*/;

        if (m_vaTextData.vt == VT_BSTR)

        {

            // converts double-byte character to single-byte

            //  character

            dlg.m_strData = m_vaTextData.bstrVal;

        }

        dlg.m_lData = m_lData;

 

        if (dlg.DoModal() == IDOK)

         {

            m_vaTextData = COleVariant(dlg.m_strData).Detach();

            m_lData = dlg.m_lData;

            bRet = TRUE;

        }

        else

        {

            bRet =  FALSE;

        }

    }

    catch (CException* pe)

    {

        TRACE("Exception: failure to display dialog\n");

        bRet =  FALSE;

        pe->Delete();

    }

    AfxUnlockTempMaps();

    return bRet;

}

 

 

Listing 20. The mymfc29BAuto.cpp implementation file.

 

The two properties, LongData and TextData, are represented by class data members m_lData and m_vaTextData, both initialized in the constructor. When the LongData property was added in ClassWizard, a notification function, OnLongDataChanged(), was specified. This function is called whenever the controller changes the property value. Notification functions apply only to properties that are represented by data members. Don't confuse this notification with the notifications that ActiveX controls give their container when a bound property changes. The DisplayDialog() member function, which is the DisplayDialog method, is ordinary except that the AfxLockTempMaps() and AfxUnlockTempMaps() functions are necessary for cleaning up temporary object pointers that would normally be deleted in an EXE program's idle loop.

 

 

 

 

 

 

 

 

 

 

 

Further reading and digging:

  1. DCOM at MSDN.

  2. COM+ at MSDN.

  3. COM at MSDN.

  4. Win32 process, thread and synchronization story can be found starting from Module R.

  5. MSDN MFC 7.0 class library online documentation.

  6. MSDN MFC 9.0 class library online documentation - latest version.

  7. MSDN Library

  8. Windows data type.

  9. Win32 programming Tutorial.

  10. The best of C/C++, MFC, Windows and other related books.

  11. Unicode and Multibyte character set: Story and program examples.

 

 


 

| Tenouk C & C++ | MFC Home | Automation 4 | Automation 6 | Download | Site Index |