| Tenouk C & C++ | MFC Home | Automation 3 | Automation 5 | Download | Site Index |


 

 

 

 

 

 

Automation 4

 

 

 

 

 

 

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 MYMFC29B Automation Component DLL Example

  2. Parameters Passed by Reference

  3. The MYMFC29B steps From Scratch

 

 

The MYMFC29B Automation Component DLL Project Example

 

You could easily convert MYMFC29A from an EXE to a DLL. The CBank class would be exactly the same, and the Excel driver would be similar. It's more interesting, though, to write a new application, this time with a minimal user interface (UI). We'll use a modal dialog box because it's the most complex UI we can conveniently use in an Automation DLL.

 

Parameters Passed by Reference

 

So far, you've seen VBA parameters passed by value. VBA has pretty strange rules for calling methods. If the method has one parameter, you can use parentheses; if it has more than one, you can't (unless you're using the function's return value, in which case you must use parentheses). Here is some sample VBA code that passes the string parameter by value:

 

Object.Method1 parm1, "text"

Object.Method2("text")

Dim s as String

s = "text"

Object.Method2(s)

 

Sometimes, though, VBA passes the address of a parameter (a reference). In this example, the string is passed by reference:

 

Dim s as String

s = "text"

Object.Method1 parm1, s

 

You can override VBA's default behavior by prefixing a parameter with ByVal or ByRef. Your component can't predict if it's getting a value or a reference, it must prepare for both. The trick is to test vt to see whether its VT_BYREF bit is set. Here's a sample method implementation that accepts a string (in a VARIANT) passed either by reference or value:

 

 

void CMyComponent::Method(long nParm1, const VARIANT& vaParm2)

{

    CString str;

    if ((vaParm2.vt & 0x7f) == VT_BSTR)

    {

        if ((vaParm2.vt & VT_BYREF) != 0)

            str = *(vaParm2.pbstrVal); // by reference

        else

            str = vaParm2.bstrVal; // by value

    }

    AfxMessageBox(str);

}

 

If you declare a BSTR (BSTRs are wide, double-byte (Unicode) strings on 32-bit Windows platforms, and narrow, single-byte strings on 16-bit Windows) parameter, the MFC library does the conversion for you. Suppose your client program passed a BSTR reference to an out-of-process component and the component program changed the value. Because the component can't access the memory of the client process, COM must copy the string to the component and then copy it back to the client after the function returns. So before you declare reference parameters, remember that passing references through IDispatch is not like passing references in C++.

 

The MYMFC29B steps From Scratch

 

The following are the steps for MYMFC29B DLL program starting from scratch.

 

Figure 43: Visual C++ new MYMFC29B DLL project dialog.

 

Figure 43: Visual C++ new MYMFC29B DLL project dialog.

 

Figure 44:  MYMFC29B Regular DLL project, AppWizard step 1 of 1.

 

Figure 44:  MYMFC29B Regular DLL project, AppWizard step 1 of 1.

 

Figure 45: MYMFC29B project summary.

 

Figure 45: MYMFC29B project summary.

Add dialog and controls.

 

Figure 46:  Inserting new resource to project.

 

Figure 46:  Inserting new resource to project.

 

Figure 47:  Inserting new dialog to project.

 

Figure 47:  Inserting new dialog to project.

 

Figure 48: The dialog property.

 

Figure 48: The dialog property.

 

Add Edit controls.

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

Figure 49: The Edit control property.

 

Figure 49: The Edit control property.

 

Figure 50: The Edit control property page.

 

Figure 50: The Edit control property page.

 

Launch ClassWizard and just click OK to create a new class, CPromptDlg for the dialog.

 

Figure 51: New class addition prompt dialog.

 

Figure 51: New class addition prompt dialog.

 

Type in the CPromptDlg as a class name.

 

Figure 53: Adding CPromptDlg class, a new class.

 

Figure 53: Adding CPromptDlg class, a new class.

 

Click the Change button and change the header and source file for the CPromptDlg class. You can leave it as default.

 

Figure 52: Changing the class header and source files name.

 

Figure 52: Changing the class header and source files name.

 

Figure 54: The added CPromptDlg class to ClassWizard database.

 

Figure 54: The added CPromptDlg class to ClassWizard database.

 

Add the following member variables.

 

long m_lData;

Cstring m_strData;

 

Figure 55: Adding m_lData member variable.

 

Figure 55: Adding m_lData member variable.

 

Figure 56: Adding m_strData member variable.

 

Figure 56: Adding m_strData member variable.

 

 

Figure 57: The added member variables using ClassWizard.

 

Figure 57: The added member variables using ClassWizard.

 

Next, add new class, CMymfc29BAuto. Don’t forget to select the Creatable by type ID option for Automation support.

 

Figure 58: Adding new class with Automation support.

 

Figure 58: Adding new class with Automation support.

 

Add the following method and property through the Automation page of the ClassWizard.

 

Method/Property

Description

LongData

Long integer property.

TextData

VARIANT property.

DisplayDialog

Method - no parameters, BOOL return.

 

Table 3.

 

Figure 59: Adding LongData property information.

 

Figure 59: Adding LongData property information.

 

Figure 60: Adding TextData property information.

 

Figure 60: Adding TextData property information.

 

Figure 61:  Adding DisplayDialog method information.

 

Figure 61:  Adding DisplayDialog method information.

 

Add the ExitInstance() handler to CMymfc29BApp class.

 

Figure 62: Adding ExitInstance() message handler.

 

Figure 62: Adding ExitInstance() message handler.

 

Click the Edit Code button and add the following code.

 

int CMymfc29BApp::ExitInstance()

{

      // TODO: Add your specialized code here and/or call the base class

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

      return CWinApp::ExitInstance();

}

 

MFC and Automation program example - C++ code snippet

 

Listing 10.

 

Modify the InitInstance().

 

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 11.

 

Add the #include statement for AfxOleRegisterTypeLib() in StdAfx.h

 

#include <afxctl.h>

 

MFC and Automation program example - C++ code snippet

 

Listing 12.

 

Add the header file of the PromptDl.h into Mymfc29BAuto.cpp implementation file.

 

MFC and Automation program example - C++ code snippet

 

Listing 13.

 

Add the following line of code in the constructor.

 

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();

}

 

MFC and Automation program example - C++ code snippet

 

Listing 14.

 

Add code for the property and method that is OnLongDataChanged(), OnTextDataChanged() and DisplayDialog().

 

void CMymfc29BAuto::OnLongDataChanged()

{

      // TODO: Add notification handler code

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

}

 

void CMymfc29BAuto::OnTextDataChanged()

{

      // TODO: Add notification handler code

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

}

 

MFC and Automation program example - C++ code snippet

 

Listing 15.

 

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;

}

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

----------------------------------------------------------------------------------------------------------------------------------------------

MFC and Automation program example - C++ code snippet

 

Listing 16.

 

Build the program and register the DLL. Make sure there is no error during the program building and DLL registering.

 

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

 

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

 

Launch Excel and save the Workbook as mymfc29B.xls. Launch the Visual Basic Editor, copy and paste the following three macros.

 

Dim Dllcomp As Object

Private Declare Sub CoFreeUnusedLibraries Lib "OLE32" ()

 

Sub LoadDllComp()

    Set Dllcomp = CreateObject("Mymfc29B.Auto")

    Range("B3").Select

    Dllcomp.LongData = Selection.Value

    Range("C3").Select

    Dllcomp.TextData = Selection.Value

End Sub

 

Sub RefreshDllComp() 'Gather Data button

    Range("B3").Select

    Dllcomp.LongData = Selection.Value

    Range("C3").Select

    Dllcomp.TextData = Selection.Value

    Dllcomp.DisplayDialog

    Range("B3").Select

    Selection.Value = Dllcomp.LongData

    Range("C3").Select

    Selection.Value = Dllcomp.TextData

End Sub

 

Sub UnloadDllComp()

    Set Dllcomp = Nothing

    Call CoFreeUnusedLibraries

End Sub

 

Figure 64: The VBA macro code.

 

Figure 64: The VBA macro code.

 

Arrange the three push buttons as shown below and assign appropriate macro to the buttons. Be careful about the column and the button arrangement. They should match with the Excel cells code in macros.

 

Figure 65: Attaching macro to button in Excel.

 

Figure 65: Attaching macro to button in Excel.

 

 

Finally we are ready to test our program. Before that, let have some story for what we have done.

 

 

 

 

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 3 | Automation 5 | Download | Site Index |