| Tenouk C++ & C | MFC Home | C++, MFC & DLL 3 | MFC Without Document/View Classes | Download | Site Index |


 

 

 

 

 

 

Module 16c:

Windows Dynamic Link Libraries - DLL 4

 

 

 

 

 

 

This is a continuation from the previous module... Program examples compiled using Visual C++ 6.0 (MFC 6.0) compiler on Windows XP Pro machine with Service Pack 2. Topics and sub topics for this Tutorial are listed below. If you think the terms used in this DLL tutorial quite blur, you can try studying the Win32 DLL first.

  1. The MYMFC22D Example: A Custom Control

  2. Revising the Updated MYMFC22B Example: Adding Code to Test mymfc22D.dll

 

The MYMFC22D Example: A Custom Control

 

The MYMFC22D program is an MFC regular DLL that implements a traffic light control indicating off, red, yellow, and green states. When clicked with the left mouse button, the DLL sends a clicked notification message to its parent and responds to two user-defined messages, RYG_SETSTATE and RYG_GETSTATE. The state is an integer that represents the color. Credit goes to Richard Wilton, who included the original C-language version of this control in his book Windows 3 Developer's Workshop (Microsoft Press, 1991). The MYMFC22D project was originally generated using AppWizard, with linkage to the shared MFC DLL, just like MYMFC22C.

 

MYMFC22D DLL project, AppWizard step 1 of 1.

 

Figure 32: MYMFC22D DLL project, AppWizard step 1 of 1.

 

 

MYMFC22D project summary.

 

Figure 33: MYMFC22D project summary.

 

Listing 9 shows the code for the primary source file, with the added code in the InitInstance() function in orange. The dummy exported Mymfc22DEntry() function exists solely to allow the DLL to be implicitly linked. The client program must include a call to this function. That call must be in an executable path in the program or the compiler will eliminate the call. As an alternative, the client program could call the Win32 LoadLibrary() function in its InitInstance() function to explicitly link the DLL.

 

MYMFC22D.CPP

 

// mymfc22D.cpp : Defines the initialization routines for the DLL.

//

 

#include "stdafx.h"

#include "mymfc22D.h"

#include "RygWnd.h"

 

#ifdef _DEBUG

#define new DEBUG_NEW

#undef THIS_FILE

static char THIS_FILE[ ] = __FILE__;

#endif

 

extern "C" __declspec(dllexport) void Mymfc22DEntry() { } // dummy function

 

(generated comment lines omitted)

 

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

// CMymfc22DApp

 

BEGIN_MESSAGE_MAP(CMymfc22DApp, CWinApp)

    //{{AFX_MSG_MAP(CMymfc22DApp)

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

        //    DO NOT EDIT what you see in these blocks of generated code!

    //}}AFX_MSG_MAP

END_MESSAGE_MAP()

 

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

// CMymfc22DApp construction

 

CMymfc22DApp::CMymfc22DApp()

{

    // TODO: add construction code here,

    // Place all significant initialization in InitInstance

}

 

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

// The one and only CMymfc22DApp object

 

CMymfc22DApp theApp;

 

BOOL CMymfc22DApp::InitInstance()

{

    CRygWnd::RegisterWndClass(AfxGetInstanceHandle());

    return CWinApp::InitInstance();

}

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

Listing 9: The mymfc22D.cpp, primary source listing.

 

Message mapping for InitInstance() function.

 

Figure 34: Message mapping for InitInstance() function.

 

The following is the InitInstance() code.

 

MFC Visual C++ code segment

 

Listing 10.

 

Listing 11 shows the code for the CRygWnd class, including the global RygWndProc() function. Click the Add Class button in ClassWizard to create this class.

 

Creating and adding new class, CRygWnd to the project using generic CWnd as its base class.

 

Figure 35: Creating and adding new class, CRygWnd to the project using generic CWnd as its base class.

 

CRygWnd class integrated into the project.

 

Figure 36: CRygWnd class integrated into the project.

 

The code that paints the traffic light isn't very interesting, so we'll concentrate on the functions that are common to most custom controls. The static RegisterWndClass() member function actually registers the RYG window class and must be called as soon as the DLL is loaded. The OnLButtonDown() handler is called when the user presses the left mouse button inside the control window. It sends the clicked notification message to the parent window. The overridden PostNcDestroy() function is important because it deletes the CRygWnd object when the client program destroys the control window. The OnGetState() and OnSetState() functions are called in response to user-defined messages sent by the client. Remember to copy the DLL to your system directory. If you feel tedious to go step by step in completing the codes, just copy and paste the following codes into the RygWnd.h and RygWnd.cpp files.

 

RYGWND.H

#if !defined(AFX_RYGWND_H__1AA889D5_9788_11D0_BED2_00C04FC2A0C2__INCLUDED_)

#define AFX_RYGWND_H__1AA889D5_9788_11D0_BED2_00C04FC2A0C2__INCLUDED_

 

#if _MSC_VER > 1000

#pragma once

#endif // _MSC_VER > 1000

// RygWnd.h : header file

//

 

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

// CRygWnd window

 

#define RYG_SETSTATE WM_USER + 0

#define RYG_GETSTATE WM_USER + 1

 

LRESULT CALLBACK AFX_EXPORT

    RygWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam);

 

class CRygWnd : public CWnd

{

private:

    int m_nState; // 0=off, 1=red, 2=yellow, 3=green

    static CRect  s_rect;

    static CPoint s_point;

    static CRect  s_rColor[3];

    static CBrush s_bColor[4];

 

// Construction

public:

    CRygWnd();

public:

    static BOOL RegisterWndClass(HINSTANCE hInstance);

 

// Attributes

public:

 

// Operations

public:

 

// Overrides

    // ClassWizard generated virtual function overrides

    //{{AFX_VIRTUAL(CRygWnd)

    protected:

    virtual void PostNcDestroy();

    //}}AFX_VIRTUAL

 

// Implementation

public:

    virtual ~CRygWnd();

 

    // Generated message map functions

private:

    void SetMapping(CDC* pDC);

    void UpdateColor(CDC* pDC, int n);

protected:

    //{{AFX_MSG(CRygWnd)

    afx_msg void OnPaint();

    afx_msg void OnLButtonDown(UINT nFlags, CPoint point);

    //}}AFX_MSG

    afx_msg LRESULT OnSetState(WPARAM wParam, LPARAM lParam);

    afx_msg LRESULT OnGetState(WPARAM wParam, LPARAM lParam);

    DECLARE_MESSAGE_MAP()

};

 

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

//{{AFX_INSERT_LOCATION}}

// Microsoft Visual C++ will insert additional declarations

//  immediately before the previous line.

#endif // !defined(AFX_RYGWND_H__1AA889D5_9788_11D0_BED2_00C04FC2A0C2__INCLUDED_)

 

 

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

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

RYGWND.CPP

// RygWnd.cpp : implementation file

//

 

#include "stdafx.h"

#include "mymfc22D.h"

#include "RygWnd.h"

 

#ifdef _DEBUG

#define new DEBUG_NEW

#undef THIS_FILE

static char THIS_FILE[ ] = __FILE__;

#endif

 

LRESULT CALLBACK AFX_EXPORT

    RygWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)

{

    AFX_MANAGE_STATE(AfxGetStaticModuleState());

    CWnd* pWnd;

 

    pWnd = CWnd::FromHandlePermanent(hWnd);

    if (pWnd == NULL) {

        // Assume that client created a CRygWnd window

        pWnd = new CRygWnd();

        pWnd->Attach(hWnd);

    }

    ASSERT(pWnd->m_hWnd == hWnd);

    ASSERT(pWnd == CWnd::FromHandlePermanent(hWnd));

    LRESULT lResult = AfxCallWndProc(pWnd, hWnd, message, wParam, lParam);

    return lResult;

}

 

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

// CRygWnd

 

// static data members

CRect  CRygWnd::s_rect(-500, 1000, 500, -1000); // outer rectangle

CPoint CRygWnd::s_point(300, 300); // rounded corners

CRect  CRygWnd::s_rColor[ ] = {CRect(-250, 800, 250, 300),

                              CRect(-250, 250, 250, -250),

                              CRect(-250, -300, 250, -800)};

CBrush CRygWnd::s_bColor[ ] = {RGB(192, 192, 192),

                              RGB(0xFF, 0x00, 0x00),

                              RGB(0xFF, 0xFF, 0x00),

                              RGB(0x00, 0xFF, 0x00)};

 

BOOL CRygWnd::RegisterWndClass(HINSTANCE hInstance) // static member function

{

    WNDCLASS wc;

    wc.lpszClassName = "RYG"; // matches class name in client

    wc.hInstance = hInstance;

    wc.lpfnWndProc = RygWndProc;

    wc.hCursor = ::LoadCursor(NULL, IDC_ARROW);

    wc.hIcon = 0;

    wc.lpszMenuName = NULL;

    wc.hbrBackground = (HBRUSH) ::GetStockObject(LTGRAY_BRUSH);

    wc.style = CS_GLOBALCLASS;

    wc.cbClsExtra = 0;

    wc.cbWndExtra = 0;

    return (::RegisterClass(&wc) != 0);

}

 

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

 

CRygWnd::CRygWnd()

{

    m_nState = 0;

    TRACE("CRygWnd constructor\n");

}

 

CRygWnd::~CRygWnd()

{

    TRACE("CRygWnd destructor\n");

}

 

BEGIN_MESSAGE_MAP(CRygWnd, CWnd)

    //{{AFX_MSG_MAP(CRygWnd)

    ON_WM_PAINT()

    ON_WM_LBUTTONDOWN()

    //}}AFX_MSG_MAP

    ON_MESSAGE(RYG_SETSTATE, OnSetState)

    ON_MESSAGE(RYG_GETSTATE, OnGetState)

END_MESSAGE_MAP()

void CRygWnd::SetMapping(CDC* pDC)

{

    CRect clientRect;

    GetClientRect(clientRect);

    pDC->SetMapMode(MM_ISOTROPIC);

    pDC->SetWindowExt(1000, 2000);

    pDC->SetViewportExt(clientRect.right, -clientRect.bottom);

    pDC->SetViewportOrg(clientRect.right / 2, clientRect.bottom / 2);

}

 

void CRygWnd::UpdateColor(CDC* pDC, int n)

{

    if (m_nState == n + 1) {

        pDC->SelectObject(&s_bColor[n+1]);

    }

    else {

        pDC->SelectObject(&s_bColor[0]);

    }

    pDC->Ellipse(s_rColor[n]);

}

 

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

// CRygWnd message handlers

 

void CRygWnd::OnPaint()

{

    int i;

    CPaintDC dc(this); // device context for painting

    SetMapping(&dc);

    dc.SelectStockObject(DKGRAY_BRUSH);

    dc.RoundRect(s_rect, s_point);

    for (i = 0; i < 3; i++) {

        UpdateColor(&dc, i);

    }

}

 

void CRygWnd::OnLButtonDown(UINT nFlags, CPoint point)

{

    // Notification code is HIWORD of wParam, 0 in this case

    GetParent()->SendMessage(WM_COMMAND, GetDlgCtrlID(),

        (LONG) GetSafeHwnd()); // 0

}

void CRygWnd::PostNcDestroy()

{

    TRACE("CRygWnd::PostNcDestroy\n");

    delete this; // CWnd::PostNcDestroy does nothing

}

 

LRESULT CRygWnd::OnSetState(WPARAM wParam, LPARAM lParam)

{

    TRACE("CRygWnd::SetState, wParam = %d\n", wParam);

    m_nState = (int) wParam;

    Invalidate(FALSE);

    return 0L;

}

 

LRESULT CRygWnd::OnGetState(WPARAM wParam, LPARAM lParam)

{

    TRACE("CRygWnd::GetState\n");

    return m_nState;

}

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

Listing 11: The CRygWnd class listing.

 

As usual, build the program and copy the mymfc22D.dll into the Windows system directory.

 

The generated DLL file of MYMFC22D program.

 

Figure 37: The generated DLL file of MYMFC22D program.

 

Revising the Updated MYMFC22B Example: Adding Code to Test mymfc22D.dll

 

The MYMFC22B program already links to the MYMFC22A and MYMFC22C DLLs. Now you'll revise the project to implicitly link to the MYMFC22D custom control.

 

Here are the steps for updating the MYMFC22B example:

 

Add a new dialog resource and class to \mfcproject\mymfc22B.Use the dialog editor to create the IDD_MYMFC22D template with a custom control with child window ID IDC_RYG, as shown here.

 

Adding a new dialog resource and modifying its properties.

 

Figure 38: Adding a new dialog resource and modifying its properties.

 

Specify RYG as the window class name of the custom control, as shown.

 

Then use ClassWizard to generate a class CTest22dDialog, derived from CDialog.

 

Creating and adding new class CTest22dDialog, derived from CDialog.

 

Figure 39: Creating and adding new class CTest22dDialog, derived from CDialog.

 

Edit the Test22dDialog.h file. Add the following private data member (added manually instead of using ClassView):

 

enum { OFF, RED, YELLOW, GREEN };

 

MFC Visual C++ code segment

 

Listing 12.

 

and m_nState variable in Test22dDialog.h,

 

Adding member variable through the ClassView.

 

Figure 40: Adding member variable through the ClassView.

 

MFC Visual C++ code segment

 

Listing 13.

 

Also add the following import and user-defined message IDs:

 

extern "C" __declspec(dllimport) void Mymfc22DEntry(); // dummy function

#define RYG_SETSTATE WM_USER + 0

#define RYG_GETSTATE WM_USER + 1

 

 

MFC Visual C++ code segment

 

Listing 14.

 

Edit the constructor in Test22dDialog.cpp to initialize the state data member. Add the following code:

 

CTest22dDialog::CTest22dDialog(CWnd* pParent /*=NULL*/)

    : CDialog(CTest22dDialog::IDD, pParent)

{

    //{{AFX_DATA_INIT(CTest22dDialog)

      // NOTE: the ClassWizard will add member initialization here

    //}}AFX_DATA_INIT

    m_nState = OFF;

    Mymfc22DEntry(); // Make sure DLL gets loaded

}

 

 

MFC Visual C++ code segment

 

Listing 15.

 

Map the control's clicked notification message. You can't use ClassWizard here, so you must add the message map entry and handler function in the Test22dDialog.cpp file, as shown here:

 

ON_CONTROL(0, IDC_RYG, OnClickedRyg) // Notification code is 0

 

MFC Visual C++ code segment

 

Listing 16.

 

void CTest22dDialog::OnClickedRyg()

{

    switch(m_nState) {

    case OFF:

        m_nState = RED;

        break;

    case RED:

        m_nState = YELLOW;

        break;

    case YELLOW:

        m_nState = GREEN;

        break;

    case GREEN:

        m_nState = OFF;

        break;

    }

    GetDlgItem(IDC_RYG)->SendMessage(RYG_SETSTATE, m_nState);

    return;

}

 

MFC Visual C++ code segment

 

Listing 17.

 

 

When the dialog gets the clicked notification message, it sends the RYG_SETSTATE message back to the control in order to change the color. Don't forget to add this prototype in the Test22dDialog.h file:

 

afx_msg void OnClickedRyg();

 

MFC Visual C++ code segment

 

Listing 18.

 

Integrate the CTest22dDialog class into the MYMFC22B application. You'll need to add a second item on the Test menu, a Mymfc22D DLL option with ID ID_TEST_MYMFC22DDLL.

 

Adding a second item on the Test menu, a Mymfc22D DLL option with ID ID_TEST_MYMFC22DDLL.

 

Figure 41: Adding a second item on the Test menu, a Mymfc22D DLL option with ID ID_TEST_MYMFC22DDLL.

 

Use ClassWizard to map this option to a member function in the CMymfc22BView class, and then code the handler in Mymfc22BView.cpp as follows:

 

Mapping ID_TEST_MYMFC22DDLL to a member function in the CMymfc22BView class.

 

Figure 42: Mapping ID_TEST_MYMFC22DDLL to a member function in the CMymfc22BView class.

 

void CMymfc22BView::OnTestMymfc22Ddll()

{

    CTest22dDialog dlg;

    dlg.DoModal();

}

 

MFC Visual C++ code segment

 

Listing 19.

 

Of course, you'll have to add the following #include line to Mymfc22BView.cpp:

 

#include "Test22dDialog.h"

 

MFC Visual C++ code segment

 

Listing 20.

 

Add the MYMFC22D import library to the linker's input library list. Choose Settings from Visual C++'s Project menu, and then add \mfcproject\mymfc22D\Debug\mymfc22D.lib to the Object/Library modules control on the Link page. With this addition, the program should implicitly link to all three DLLs.

 

Adding the mymfc22D.lib (import) library to the linker's input library list, we have three DLL libraries here.

 

Figure 43: Adding the mymfc22D.lib (import) library to the linker's input library list, we have three DLL libraries here.

 

Build and test the updated MYMFC22B application. Choose Mymfc22D DLL from the Test menu. Try clicking the traffic light with the left mouse button. The traffic-light color should change. The result of clicking the traffic light several times is shown here.

 

MYMFC22B program output with three DLLs linking.

 

Figure 44: MYMFC22B program output with three DLLs linking.

 

MYMFC22B program output using the third type DLL.

 

Figure 45: MYMFC22B program output using the third type of DLL.

 

 

 

 

Further reading and digging:

  1. Win32 dynamic link library, DLL.

  2. MSDN MFC 7.0 class library online documentation.

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

  4. MSDN Library

  5. Windows data type.

  6. Win32 programming Tutorial.

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

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

 

 


 

| Tenouk C++ & C | MFC Home | C++, MFC & DLL 3 | MFC Without Document/View Classes | Download | Site Index |