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.
|
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.
Figure 32: MYMFC22D DLL project, AppWizard step 1 of 1. |
|

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.

Figure 34: Message mapping for InitInstance() function.
The following is the InitInstance() code.

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.

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

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.

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.

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.

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 };

Listing 12.
and m_nState variable in Test22dDialog.h,

Figure 40: Adding member variable through the ClassView.

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

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
}

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

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

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.

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:

Figure 42: Mapping ID_TEST_MYMFC22DDLL to a member function in the CMymfc22BView class.
void CMymfc22BView::OnTestMymfc22Ddll()
{
CTest22dDialog dlg;
dlg.DoModal();
}

Listing 19.
Of course, you'll have to add the following #include line to Mymfc22BView.cpp:
#include "Test22dDialog.h"

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.

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.

Figure 44: MYMFC22B program output with three DLLs linking.

Figure 45: MYMFC22B program output using the third type of DLL.
Further reading and digging:
MSDN What's New (MFC Feature Pack) - feature pack.
DCOM at MSDN.
COM+ at MSDN.
COM at MSDN.
Unicode and Multi-byte character set: Story and program examples.