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





Automation 3





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 MYMFC29A Automation Component - EXE Example: No User Interface...continue

  2. MYMFC29A Story

  3. Debugging an EXE Component Program









Set up five Excel macros in a new workbook file, mymfc29A.xls. Launch the Excel and save the workbook as mymfc29A.xls. Launch the Visual Basic Editor (Alt + F11). Select the Sheet1 and add the following macro code:


Dim Bank As Object

Sub LoadBank()

    Set Bank = CreateObject("Mymfc29A.Bank")

End Sub


Sub UnloadBank()

    Set Bank = Nothing

End Sub


Sub DoDeposit()


    Bank.Deposit (ActiveCell.Value)

End Sub


Sub DoWithdrawal()


    Dim Amt

    Amt = Bank.Withdrawal(ActiveCell.Value)


    ActiveCell.Value = Amt

End Sub


Sub DoInquiry()

    Dim Amt

    Amt = Bank.Balance()


    ActiveCell.Value = Amt

End Sub

Figure 37: Excel (client) macro code for MYMFC29A (component) testing purpose.


Figure 37: Excel (client) macro code for MYMFC29A (component) testing purpose.


Arrange an Excel worksheet as shown in Figure 39. Attach the macros to the pushbuttons appropriately (by right-clicking the pushbuttons). You can also see all the macros through the Tools Macro menu.


Figure 38: Viewing macros through Macros menu.


Figure 38: Viewing macros through Macros menu.


Test the MYMFC29A bank component. Click the Load Bank Program button, and then enter a deposit value in cell B5 and click the Deposit button. Click the Balance Inquiry button, and watch the balance appear in cell D5.


Figure 39: Testing MYMFC29A bank component.


Figure 39: Testing MYMFC29A bank component.


Enter a withdrawal value in cell C5, and click the Withdrawal button. To see the balance, click the Balance Inquiry button.









Figure 40: Testing MYMFC29A bank component.


Figure 40: Testing MYMFC29A bank component.


Sometimes you need to click the buttons twice. The first click switches the focus to the worksheet, and the second click runs the macro. If any, the hourglass pointer tells you the macro is working.

What's happening in this program? Look closely at the CMymfc29AApp::InitInstance function. When you run the program directly from Windows, it displays a message box and then quits, but not before it updates the Registry. The COleObjectFactory::UpdateRegistryAll function hunts for global class factory objects, and the CBank class's IMPLEMENT_OLECREATE macro invocation defines such an object. The IMPLEMENT_OLECREATE line was generated because you checked ClassWizard's Createable By Type ID check box when you added the CBank class. The unique class ID and the program ID, MYMFC29A.BANK, are added to the Registry.


Figure 41: Viewing the registered component in Registry.


Figure 41: Viewing the registered component in Registry.


MYMFC29A Story


When Excel now calls CreateObject(), COM loads the MYMFC29A program, which contains the global factory for CBank objects; COM then calls the factory object's CreateInstance() function to construct the CBank object and return an IDispatch pointer. Here's the CBank class declaration that ClassWizard generated in the Bank.h file, with unnecessary detail (and the method and property functions you've already seen) omitted:


class CBank : public CCmdTarget




    double m_dBalance;

    // protected constructor used by dynamic creation



// Attributes



// Operations



// Overrides

    // ClassWizard generated virtual function overrides



    virtual void OnFinalRelease();



// Implementation


    virtual ~CBank();


    // Generated message map functions


        // NOTE - the ClassWizard will add and remove member

        //  functions here.






    // Generated OLE dispatch map functions


    afx_msg double GetBalance();

    afx_msg void SetBalance(double newValue);

    afx_msg double Withdrawal(double dAmount);

    afx_msg void Deposit(double dAmount);






Here is the code automatically generated by ClassWizard in Bank.cpp:







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

    //  object is active, the constructor calls AfxOleLockApp.






    // To terminate the application when all objects created with

    //  OLE automation, the destructor calls AfxOleUnlockApp.





void CBank::OnFinalRelease()


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

    //  OnFinalRelease is called. This implementation deletes the

    //  object. Add additional cleanup required for your object

    //  before deleting it from memory.






        // NOTE - the ClassWizard will add and remove

        //  mapping macros here.






    DISP_PROPERTY_EX(CBank, "Balance", GetBalance, SetBalance, VT_R8)

    DISP_FUNCTION(CBank, "Withdrawal", Withdrawal, VT_R8, VTS_R8)

    DISP_FUNCTION(CBank, "Deposit", Deposit, VT_EMPTY, VTS_R8)




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

//  from VBA.  This IID must match the GUID that is attached to the

//  dispinterface in the .ODL file.


// {A9515AB6-5B85-11D0-848F-00400526305B}

static const IID IID_IBank = { 0xa9515ab6, 0x5b85, 0x11d0, { 0x84, 0x8f, 0x0, 0x40, 0x5, 0x26, 0x30, 0x5b } };



    INTERFACE_PART(CBank, IID_IBank, Dispatch)



// {632B1E4C-F287-11CE-B5E3-00AA005B1574}

IMPLEMENT_OLECREATE2(CBank, "MYMFC29A.BANK", 0x632b1e4c, 0xf287, 0x11ce, 0xb5, 0xe3, 0x0, 0xaa, 0x0, 0x5b, 0x15, 0x74)


This first version of the MYMFC29A program runs in single-process mode, as does the Autoclik program. If a second Automation client asks for a new CBank object, COM calls the class factory CreateInstance() function again and the existing process constructs another CBank object on the heap. You can verify this by making a copy of the mymfc29A.xls workbook (under a different name) and loading both the original and the copy. Click the Load Bank Program button in each workbook, and watch the Debug window. InitInstance() should be called only once. A small change in the MYMFC29A program makes it behave differently. To have a new MYMFC29A process start up each time a new component object is requested, follow these steps.


Add the following macro in Bank.h:


#define IMPLEMENT_OLECREATE2(class_name, external_name, \

    l, w1, w2, b1, b2, b3, b4, b5, b6, b7, b8) \

    AFX_DATADEF COleObjectFactory class_name::factory(class_name::guid, \

    RUNTIME_CLASS(class_name), TRUE, _T(external_name)); \

    const AFX_DATADEF GUID class_name::guid = \

    { l, w1, w2, { b1, b2, b3, b4, b5, b6, b7, b8 } };








MFC and Automation program example - C++ code snippet


Listing 8.


This macro is the same as the standard MFC IMPLEMENT_OLECREATE macro except that the original FALSE parameter (after the RUNTIME_CLASS parameter) has been changed to TRUE.


In Bank.cpp, change the IMPLEMENT_OLECREATE macro invocation to IMPLEMENT_OLECREATE2.


MFC and Automation program example - C++ code snippet


Listing 9.


Build the program and test it using Excel. Start two Excel processes and then load the bank program from each. Use the Microsoft Windows Task Manager or SPY++ (Visual C++ Tool) to verify that two MYMFC29A processes are running.


Debugging an EXE Component Program


When an Automation client launches an EXE component program, it sets the /Embedding command-line parameter. If you want to debug your component, you must do the same. Choose Settings from the Visual C++ Project menu, and then enter /Embedding in the Program Arguments box on the Debug page, as shown here.


Figure 42: Changing the project Debug setting.


Figure 42: Changing the project Debug setting.



When you click the Go button on the Debug toolbar, your program will start and then wait for a client to activate it. At this point, you should start the client program from Windows (if it is not already running) and then use it to create a component object. Your component program in the debugger should then construct its object. It might be a good idea to include a TRACE statement in the component object's constructor.

Don't forget that your component program must be registered before the client can find it. That means you have to run it once without the /Embedding flag. Many clients don't synchronize with Registry changes. If your client is running when you register the component, you may have to restart the client.



Further reading and digging:

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

  2. MSDN MFC 7.0 class library online documentation.

  3. MSDN What's New (MFC Feature Pack) - feature pack.

  4. Porting & Migrating your older programs.

  5. MSDN Library

  6. DCOM at MSDN.

  7. COM+ at MSDN.

  8. COM at MSDN.

  9. Windows data type.

  10. Win32 programming Tutorial.

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

  12. Unicode and Multi-byte character set: Story and program examples.



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