Examining MFC 2.0

Micrsoft claims that any application written under version 1.0 of the Microsoft Foundation Class Library (MFC) will run unmodified under MFC 2.0. He shows how he ported PT, a Windows-based periodic table originally written using MFC 1.0, to MFC 2.0.


June 01, 1993
URL:http://www.drdobbs.com/windows/examining-mfc-20/184409020

Figure 1


Figure 2


Copyright © 1993, Dr. Dobb's Journal

JUN93: EXAMINING MFC 2.0

EXAMINING MFC 2.0

This class library has all the elements you need to build a graphical periodic table

Michael Yam

Michael is an independent consultant and has served New York's financial district since 1984. He can be reached on CompuServe at 76367,3040.


The Microsoft Foundation Class library (MFC) provides a wrapper for most, but not all, Windows API functions. The result is a condensed, object-oriented API that's at least familiar to seasoned SDK programmers. You're in for a surprise, however, when you start looking for the SDK's WinMain, WndProc, or a message loop because MFC doesn't need the skeletal code required by the Windows API. This could be disconcerting for experienced SDK programmers because it suggests a loss of control. That's not the case, however.

MFC 1.0, originally introduced with Microsoft C/C++ 7.0, took a minimalist approach to supporting the implementation of user-interface elements within Windows. MFC 2.0, released with Visual C++, is more robust. While MFC 1.0 contained about 60 C++ classes, MFC 2.0 provides over 100. According to Microsoft, any application written under earlier versions of MFC will run virtually unmodified under 2.0--well, almost. I've found that the functionality of some classes has been either merged or completely replaced. Although the changes are minor, they can be "gotchas" when moving your code to MFC 2.0. Furthermore, a redesign of your application may be required to take full advantage of the more recent version of MFC.

A Look at PT

As a "Petzoldian" and a chemistry major of long ago (in my time, only 105 elements existed), writing a periodic table was a good way to examine MFC. PT, a periodic table like that in most chemistry textbooks, is a Windows application I originally wrote using MFC 1.0. See Table 1(a) for a list of the files that make up PT. PT uses a modeless dialog box as its main window. Doing so takes advantage of the built-in functionality offered by dialog boxes, such as allowing the user to move among controls with the Tab key. It's also easier to get and set text in edit controls. And in terms of program size and memory requirements, a dialog box as a main window is thriftier than a standard frame window.

Table 1: (a) Files that make up PT; (b) data members in the CWinApp class; (c) MFC global functions to access WinMain() variables.

  (a)

     PT.H
     PT.CPP
     PT.RC
     PT.DEF
     MAKEFILE
     DIALOGS.DLG
     RESOURCE.H
     PT.ICO

  (b)

     Data Members          Description
     ---------------------------------------------------------------------

     m_pszAppName          Specifies the name of the application.
     m_hInstance           Corresponds to the hInstance parameter passed
                            by Windows to WinMain().
     m_hPrevInstance       Corresponds to the hPrevInstance parameter
                            passed by Windows to WinMain().
     m_IpCmdLine           Corresponds to the IpCmdLine parameter passed
                            by Windows to WinMain().
     m_nCmdShow            Corresponds to the nCmdShow parameter passed
                            by Windows to WinMain().
     m_pMainWnd            Holds a pointer to the application's main
                            window.

  (c)

     Functions             Description

     AfxGetApp             Obtain a pointer to the CWinApp object.
     AfxGetInstanceHandle  Obtain a handle to the current application
                            instance.
     AfxGetResourceHandle  Obtain a handle to the application's resources.
     AfxGetAppName         Obtain a pointer to a string containing the
                            application's name.

I arranged the display of the periodic table to resemble the map in your chemistry class; see Figure 1. The idea is to point to the element of choice, click the mouse, and have the program display edit fields containing the name, symbol, atomic number, and atomic weight of the specified element. You can also retrieve information by typing data into the appropriate edit field and pressing Enter. As a convenience for those who can't remember the spelling of, say, "Molybdenum" or recall its symbol, the name and symbol edit fields are implemented as combo boxes so that data may be chosen from the list-box portion.

Browsing MFC

Microsoft developed MFC to simplify the Windows API, enable object-oriented (C++) techniques, and provide a degree of portability from 16- to 32-bit versions of Windows. The more than 100 MFC classes can be divided into two groups: general purpose and Windows specific. The former manages file services, persistent objects, exception handling, strings, and collections. The latter should appeal to SDK programmers because it supports GDI, MDI, OLE, menus, dialogs, controls, and the like. Figure 2 shows the complete hierarchy.

The class CWinApp wraps all the functionality of WinMain() in class members; see Table 1(b). An MFC program only needs to declare an instance of CWinApp and the constructor takes care of the usual WinMain() responsibilities (class registration and main message-loop processing). And just as an SDK program can have only one WinMain(), an MFC program can only have one instance of CWinApp.

With 2.0 message maps, you write individual functions to handle each event instead of using switch/case statements to process messages in Windows procedures. A good comparison is MS Basic's ON KEY(n) GOSUB line statement; if a function key or cursor key is pressed, then GOSUB line is executed. With MFC, your individual function is executed if an event (like a function key, button press, or paint message) is detected. A side benefit of the message map is that it provides portability to Win32 by eliminating the need to decode wParam and lParam.

MFC 2.0 also supports OLE 1.0 (unlike MFC 1.0). Nine classes provide for client and server applications, and integrate OLE into MFC's document architecture and view classes. The client and server classes should not be thought of as two different categories of OLE items, but as two different interfaces to the same OLE item. Since these interfaces communicate through OLE system DLLs, a client application should never directly call member functions of a server class. Likewise, a server application should never directly call member functions of a client class. Maintaining the integrity of the OLE interprocess communication allows client applications to accept items from any server; you won't need to write code to handle the specific contents of an incoming item. This separation of client and server, now artificially imposed by the framework, will be extended and integrated into the upcoming OLE 2.0.

The server classes allow for the creation of full and mini-servers. A full server can be a stand-alone application or an application that has been launched by a client. A full server handles both embedded and linked objects, can own documents and write them to disk, and typically has a multiple document interface. A mini-server, on the other hand, is launched by a client and handles only embedded objects. A mini-server does not own documents, but rather accesses those of the client. Additionally, a mini-server typically has a single-document interface. The MS-Draw and Graph components of Microsoft Word for Windows are mini-servers.

Also new to MFC 2.0 is support for Visual Basic 1.0 custom controls or VBX controls. VBX controls are stored in Windows DLLs and the class CVBControl allows you to load the controls, and get and set their properties. Compatibility is good, although some VBX features, such as drag-and-drop and control arrays, are not supported. Naturally, any VBX control that relies on the internal or undocumented features of Visual Basic isn't guaranteed to work.

Dialog Box as the Main Window

As previously mentioned, PT uses a modeless dialog box as its main window. From Microsoft's CDialog class, PT derives its own class, CPTDialog (as shown in Listing One, page 132). To conserve space here, many of the functions for the 109 elements are available electronically; see page 7. Using an object of this class as a main window requires a further look at two components of CWinApp: an overrideable member function, InitInstance(), and a public data member, m_pMainWnd. InitInstance() creates a window object and m_pMainWnd holds the pointer to that window object. Therefore, you need to assign the main window pointer to the CPTDialog constructor to make a dialog object a main window. From CWinApp, PT derives an application class, CTheApp, and inside CTheApp::InitInstance(), places the statement m_pMainWnd=new CPTDialog().

Custom Icons

The code inside the constructor should reassure SDK programmers that MFC can be overridden; besides creating the dialog object, CPTDialog() replaces the default icon, a white box, with its own icon. MFC provides two mechanisms for loading a custom icon; unfortunately, neither work for a dialog box. Still, the techniques are worth knowing, so I'll mention them briefly. The first mechanism is through a function, AfxRegisterWndClass(), which accepts the icon as one of its arguments. Other arguments include the class style, the mouse cursor, and the background. AfxRegisterWndClass() generates a class name to be passed into a Create() member function, typically of the CWnd class. The problem is that the Create() member function of the CDialog class doesn't accept a class name.

The second mechanism involves overriding reserved MFC icon IDs defined as AFX_IDI_STD_FRAME and AFX_IDI_STD_MDIFRAME (see Figure 3). But as the names of the identifiers suggest, this works only for standard frame windows and MDI frame windows, not for dialogs. Loading a custom icon for a dialog box wasn't obvious from the documentation, possibly because a user doesn't usually need to minimize a dialog box. The solution was to register my own PT dialog class, and load my icon there.

Figure 3: Overriding icon IDs reserved by MFC.

  AFX_IDI_STD_FRAME     ICON  custom.ico
  AFX_IDI_STD_MDIFRAME  ICON  custom.ico

Inside CPTDialog, you'll find a private function member: RegisterPTClass() (shown in Listing Two, page 132). It fills in the WNDCLASS structure and resembles some of the initialization code found in WinMain(). I set the icon, as well as the cursor, background, and menu, and designated the class name as PTDLGCLASS. This class name is referenced by the dialog-box template described in the resource file DIALOGS.DLG (available electronically; see page 7). Notice that RegisterPTClass() uses "unwrapped" Windows functions such as ::LoadIcon(), ::LoadCursor(), and ::RegisterClass(). The C++ scope resolution operator, ::, indicates that the name refers to the Windows function and not to a class-member function. RegisterPTClass() also needs the application's instance handle and calls an MFC global function AfxGetInstanceHandle() to retrieve it. MFC global functions access the WinMain() variables, of which there are four; see Table 1(c).

Mapping Messages

For simplicity, I've assigned each element its own button control. This totals 109 element buttons. (Don't forget that 255 controls per dialog is the limit set by the Windows API.) Each button is entered into the message map and associated with a function to display the element's data. To establish a message map, I first included DECLARE_MESSAGE_MAP inside the CPTDialog class. Only one declaration is allowed per class. The code fragment in Figure 4 belongs outside the class declaration and outside the scope of any function.

Figure 4: Declaring a message map.

  BEGIN_MESSAGE_MAP (CPTDialog, CDialog)
     WM_CLOSE ()
     ON_COMMAND (IDM_ABOUT, OnAbout)
     ON_CBN_SETFOCUS (IDD_ELEMENTNAME,
                         OnNameSetFocus)
     ON_COMMAND (H. OnH)
     // other messages
  END_MESSAGE_MAP()

Listing Two contains the map. Four categories of messages can be trapped: WM_COMMAND messages generated by menu selections, WM_COMMAND messages generated by keys, notification messages from child windows, and general WM_messages such as WM_PAINT or WM_CLOSE. Messages to be trapped are placed between the macros BEGIN_MESSAGE_MAP and END_MESSAGE_MAP(). To help direct the flow of messages, BEGIN_MESSAGE_MAP requires two arguments: the derived class and the base class. When a message is sent to a CPTDialog object, it is compared against the CPTDialog message map. If an entry is found, the associated function is executed. Otherwise, the search continues with the message map in the parent class. As long as the message isn't matched, it continues to flow up the class hierarchy until it reaches the CWnd class--the mother of all Windows classes in MFC. One of its member functions includes DefWindowProc(). Just as in the SDK Windows procedure, this is where unmatched messages go for default processing.

The first message in Figure 4 traps WM_CLOSE, overriding CWnd::OnClose(). This is generally not necessary for a standard window frame, but is required when using a dialog box. Recall that a modeless dialog box can only be closed with DestroyWindow(). Yet when using MFC, DestroyWindow() by itself is not enough. The function only destroys, or closes, the dialog window you see on the screen; it does not destroy the dialog object. Any associated data structures and resources remain in memory. MFC 1.0 provided an elegant solution to this problem: code CPTDialog::OnClose() with the C++ statement delete this. This not only destroys the CPTDialog object (freeing resources), but also transparently calls DestroyWindow() to close the dialog window. Had I not trapped WM_CLOSE, the message would have flowed up to CWnd and triggered the destructor, ~CWnd. Since ~CWnd just calls DestroyWindow(), it would have left the dialog object as orphaned memory. However, using this technique with MFC 2.0 is no longer recommended by Microsoft. Instead, you should code DestroyWindow() and move delete this to an overrided PostNcDestroy member function (see Listing One).

The second message in the map corresponds to a menu message; CPTDialog::OnAbout() is executed whenever IDM_ABOUT is detected. OnAbout() in turn opens a modal dialog box to display information about the periodic table. A modal dialog box, About, is then constructed and accepts two parameters: the dialog template name (see Listing Three, page 134) and a pointer that identifies the current object, CPTDialog. DoModal() is the member function that runs the dialog box.

The third message in the map is just one of four combo-box/edit-field notification messages trapped by PT. By keeping track of which field has the input focus, PT can determine what kind of information the user wants to search on: the element name, symbol, atomic number, or atomic weight. The search is performed by CPTDialog::GetPTData() in Listing Two.

The fourth message starts a long list of element IDs and their associated display functions. I've identified the dialog button ID as H, instead of IDD_H (per Microsoft naming convention). In the context of PT the IDD_ prefix seemed redundant and would not enhance code readability. Also, it would have been natural to assign each element's button ID to their respective atomic numbers, starting with one for Hydrogen and ending with 109 for Une, but I couldn't. Microsoft reserves ID numbers below 100 (for example, IDOK and IDCANCEL are one and two, respectively), so I defined the element-button IDs as "atomic number+ 100."

PT data is stored in memory as an array of structures, as shown in Figure 5. The "+1" in the fields indicates they are null terminated. Setting the 0th element to Null, I filled the array by atomic number, starting with information for Hydrogen as _atom[1] and ending with Une as _atom[109]. CPTDialog::Display() accepts the element button ID as its argument and displays the information corresponding to _atom[elementid- 100].

Figure 5: Structure used to store PT data.

  struct
  {
     unsigned char number;
     char symbol [3+1];
     char element [12+1];
     char weight [9+1];
  }_atom [110];

Conclusion

I've covered only a small, but fundamental, part of MFC. If you've had the patience and curiosity to pursue the SDK, you won't have a problem coping with MFC. The most difficult aspects were accepting that WinMain() and Windows procedures were gone, and trusting that significant control hadn't been lost. Also, locating MFC member functions which paralleled the SDK functions was difficult because, while the SDK Programmer's Reference laid out functions in alphabetical order (flat), the MFC Class Libraries Reference stored member functions in class descriptions (hierarchical); MFC is, after all, object oriented.

Conventional wisdom states that to learn Windows programming and C++, you should study one, then the other, but not both at the same time. The combined concepts, from the Windows' API to the syntax of C++ to object-oriented programming, would overwhelm most people. But because MFC and C++ go hand in hand, it now makes good sense to learn and take advantage of both.

References

Chiverton, Bob. "C/C++ Questions & Answers." Microsoft Systems Journal (October, 1992).

Microsoft C/C++ Class Libraries Reference. Microsoft Corp., 1991.

"Reference Tables for Chemistry." New York State Department of Education.



_EXAMINING MFC 2.0_
by Michael Yam


[LISTING ONE]


//----- PT.H - Declares class interfaces for Periodic Table -----
#ifndef __PT_H__
#define __PT_H__

#define PT_MAXELEMENTS  107

class CPTDialog : public CDialog
{
private:
    static BOOL bRegistered;
    static BOOL RegisterPTClass();
    void Display (int iAtomicNumber);
    void GetPTData (int nID);
    char CB_Focus[4];
public:
    CPTDialog();
    void OnClose()
    {
        delete this;
    }
    void OnAbout();
    void OnHelp();
    void OnOK();
    void OnCancel();


    void OnNameSetFocus();
    void OnSymbolSetFocus();
    void OnNumberSetFocus();
    void OnWeightSetFocus();

    void OnH()
    {
        Display (H);
    }
    void OnHe()
    {
        Display (He);
    }
    void OnLi()
    {
        Display (Li);
    }
    void OnBe()
    {
        Display (Be);
    }
    void OnB()
    {
        Display (B);
    }
    void OnC()
    {
        Display (C);
    }
    void OnN()
    {
        Display (N);
    }
    void OnO()
    {
        Display (O);
    }
    void OnF()
    {
        Display (F);
    }
    void OnNe()
    {
        Display (Ne);
    }
    void OnNa()
    {
        Display (Na);
    }
    void OnMg()
    {
        Display (Mg);
    }
    void OnAl()
    {
        Display (Al);
    }
    void OnSi()
    {
        Display (Si);
    }
    void OnP()
    {
        Display (P);
    }
    void OnS()
    {
        Display (S);
    }
    void OnCl()
    {
        Display (Cl);
    }
    void OnAr()
    {
        Display (Ar);
    }
    void OnK()
    {
        Display (K);
    }
    void OnCa()
    {
        Display (Ca);
    }
    void OnSc()
    {
        Display (Sc);
    }
    void OnTi()
    {
        Display (Ti);
    }
    void OnV()
    {
        Display (V);
    }
    void OnCr()
    {
        Display (Cr);
    }
    void OnMn()
    {
        Display (Mn);
    }
    void OnFe()
    {
        Display (Fe);
    }
    void OnCo()
    {
        Display (Co);
    }
    void OnNi()
    {
        Display (Ni);
    }
    void OnCu()
    {
        Display (Cu);
    }
    void OnZn()
    {
        Display (Zn);
    }
    void OnGa()
    {
        Display (Ga);
    }
    void OnGe()
    {
        Display (Ge);
    }
    void OnAs()
    {
        Display (As);
    }
    void OnSe()
    {
        Display (Se);
    }
    void OnBr()
    {
        Display (Br);
    }
    void OnKr()
    {
        Display (Kr);
    }
    void OnRb()
    {
        Display (Rb);
    }
    void OnSr()
    {
        Display (Sr);
    }
    void OnY()
    {
        Display (Y);
    }
    void OnZr()
    {
        Display (Zr);
    }
    void OnNb()
    {
        Display (Nb);
    }
    void OnMo()
    {
        Display (Mo);
    }
    void OnTc()
    {
        Display (Tc);
    }
    void OnRu()
    {
        Display (Ru);
    }
    void OnRh()
    {
        Display (Rh);
    }
    void OnPd()
    {
        Display (Pd);
    }
    void OnAg()
    {
        Display (Ag);
    }
    void OnCd()
    {
        Display (Cd);
    }
    void OnIn()
    {
        Display (In);
    }
    void OnSn()
    {
        Display (Sn);
    }
    void OnSb()
    {
        Display (Sb);
    }
    void OnTe()
    {
        Display (Te);
    }
    void OnI()
    {
        Display (I);
    }
    void OnXe()
    {
        Display (Xe);
    }
    void OnCs()
    {
        Display (Cs);
    }
    void OnBa()
    {
        Display (Ba);
    }
    void OnLa()
    {
        Display (La);
    }
    void OnCe()
    {
        Display (Ce);
    }
    void OnPr()
    {
        Display (Pr);
    }
    void OnNd()
    {
        Display (Nd);
    }
    void OnPm()
    {
        Display (Pm);
    }
    void OnSm()
    {
        Display (Sm);
    }
    void OnEu()
    {
        Display (Eu);
    }
    void OnGd()
    {
        Display (Gd);
    }
    void OnTb()
    {
        Display (Tb);
    }
    void OnDy()
    {
        Display (Dy);
    }
    void OnHo()
    {
        Display (Ho);
    }
    void OnEr()
    {
        Display (Er);
    }
    void OnTm()
    {
        Display (Tm);
    }
    void OnYb()
    {
        Display (Yb);
    }
    void OnLu()
    {
        Display (Lu);
    }
    void OnHf()
    {
        Display (Hf);
    }
    void OnTa()
    {
        Display (Ta);
    }
    void OnW()
    {
        Display (W);
    }
    void OnRe()
    {
        Display (Re);
    }
    void OnOs()
    {
        Display (Os);
    }
    void OnIr()
    {
        Display (Ir);
    }
    void OnPt()
    {
        Display (Pt);
    }
    void OnAu()
    {
        Display (Au);
    }
    void OnHg()
    {
        Display (Hg);
    }
    void OnTl()
    {
        Display (Tl);
    }
    void OnPb()
    {
        Display (Pb);
    }
    void OnBi()
    {
        Display (Bi);
    }
    void OnPo()
    {
        Display (Po);
    }
    void OnAt()
    {
        Display (At);
    }
    void OnRn()
    {
        Display (Rn);
    }
    void OnFr()
    {
        Display (Fr);
    }
    void OnRa()
    {
        Display (Ra);
    }
    void OnAc()
    {
        Display (Ac);
    }
    void OnTh()
    {
        Display (Th);
    }
    void OnPa()
    {
        Display (Pa);
    };
    void OnU()
    {
        Display (U);
    }
    void OnNp()
    {
        Display (Np);
    }
    void OnPu()
    {
        Display (Pu);
    }
    void OnAm()
    {
        Display (Am);
    }
    void OnCm()
    {
        Display (Cm);
    }
    void OnBk()
    {
        Display (Bk);
    }
    void OnCf()
    {
        Display (Cf);
    }
    void OnEs()
    {
        Display (Es);
    }
    void OnFm()
    {
        Display (Fm);
    }
    void OnMd()
    {
        Display (Md);
    }
    void OnNo()
    {
        Display (No);
    }
    void OnLr()
    {
        Display (Lr);
    }
    void OnUnq()
    {
        Display (Unq);
    }
    void OnUnp()
    {
        Display (Unp);
    }
    void OnUnh()
    {
        Display (Unh);
    }
    void OnUns()
    {
        Display (Uns);
    }
    void OnUno()
    {
        Display (Uno);
    }
    void OnUne()
    {
        Display (Une);
    }
    DECLARE_MESSAGE_MAP()
};
class CTheApp : public CWinApp
{
public:
    BOOL InitInstance();
};
// Data stored in memory as an array of structures: _atom[]. Weights in
// parens correspond to atoms of most stable isotope. Data retrieved from
// "Reference Tables for Chemistry," SUNY, State Education Dept., Albany, NY  12234.
struct
{
    unsigned char number;           // atomic number
    char symbol[3+1];               // three char symbol plus null
    char element[12+1];             // full name plus null
    char weight[9+1];               // atomic weight
}_atom[] = {

     0, "",   "",                "",
     1, "H" , "Hydrogen",        "1.0079",
     2, "He", "Helium",          "4.00260",
     3, "Li", "Lithium",         "6.941",
     4, "Be", "Beryllium",       "9.01218",
     5, "B" , "Boron",           "10.81",
     6, "C" , "Carbon",          "12.011",
     7, "N" , "Nitrogen",        "14.0067",
     8, "O" , "Oxygen",          "15.9994",
     9, "F" , "Fluorine",        "18.998403",
    10, "Ne", "Neon",            "20.179",
    11, "Na", "Sodium",          "22.98977",
    12, "Mg", "Magnesium",       "24.305",
    13, "Al", "Aluminum",        "26.98154",
    14, "Si", "Silicon",         "28.0855",
    15, "P" , "Phosphorus",      "30.97376",
    16, "S" , "Sulfur",          "32.06",
    17, "Cl", "Chlorine",        "35.453",
    18, "Ar", "Argon",           "39.948",
    19, "K" , "Potassium",       "39.0983",
    20, "Ca", "Calcium",         "40.08",
    21, "Sc", "Scandium",        "44.9559",
    22, "Ti", "Titanium",        "47.90",
    23, "V" , "Vanadium",        "50.9414",
    24, "Cr", "Chromium",        "51.996",
    25, "Mn", "Manganese",       "54.9830",
    26, "Fe", "Iron",            "55.847",
    27, "Co", "Cobalt",          "58.9332",
    28, "Ni", "Nickel",          "58.70",
    29, "Cu", "Copper",          "63.546",
    30, "Zn", "Zinc",            "65.38",
    31, "Ga", "Gallium",         "69.72",
    32, "Ge", "Germanium",       "72.59",
    33, "As", "Arsenic",         "74.9216",
    34, "Se", "Selenium",        "78.96",
    35, "Br", "Bromine",         "79.904",
    36, "Kr", "Krypton",         "83.80",
    37, "Rb", "Rubidium",        "85.4678",
    38, "Sr", "Strontium",       "87.62",
    39, "Y" , "Yttrium",         "88.9059",
    40, "Zr", "Zirconium",       "91.22",
    41, "Nb", "Niobium",         "92.9064",
    42, "Mo", "Molybdenum",      "95.94",
    43, "Tc", "Technetium",      "(97)",
    44, "Ru", "Ruthenium",       "101.07",
    45, "Rh", "Rhodium",         "102.9055",
    46, "Pd", "Palladium",       "106.4",
    47, "Ag", "Silver",          "107.868",
    48, "Cd", "Cadmium",         "112.41",
    49, "In", "Indium",          "114.82",
    50, "Sn", "Tin",             "118.69",
    51, "Sb", "Antimony",        "121.75",
    52, "Te", "Tellurium",       "127.60",
    53, "I" , "Iodine",          "126.9045",
    54, "Xe", "Xenon",           "131.30",
    55, "Cs", "Cesium",          "132.9054",

    56, "Ba", "Barium",          "137.33",
    57, "La", "Lanthium",        "138.9055",
    58, "Ce", "Cerium",          "140.12",
    59, "Pr", "Praseodymium",    "140.9077",
    60, "Nd", "Neodymium",       "144.24",
    61, "Pm", "Promethium",      "(145)",
    62, "Sm", "Samarium",        "150.4",
    63, "Eu", "Europium",        "151.96",
    64, "Gd", "Gadolinium",      "157.25",
    65, "Tb", "Terbium",         "158.9254",
    66, "Dy", "Dysprosium",      "162.50",
    67, "Ho", "Holmium",         "164.9304",
    68, "Er", "Erbium",          "167.26",
    69, "Tm", "Thulium",         "168.9342",
    70, "Yb", "Ytterbium",       "173.04",
    71, "Lu", "Lutetium",        "174.97",
    72, "Hf", "Hafnium",         "178.49",
    73, "Ta", "Tantalum",        "180.9479",
    74, "W" , "Tungsten",        "183.85",
    75, "Re", "Rhenium",         "186.207",
    76, "Os", "Osmium",          "190.2",
    77, "Ir", "Iridium",         "192.22",
    78, "Pt", "Platinum",        "195.09",
    79, "Au", "Gold",            "196.9665",
    80, "Hg", "Mercury",         "200.59",
    81, "Tl", "Thallium",        "204.37",
    82, "Pb", "Lead",            "207.2",
    83, "Bi", "Bismuth",         "208.9804",
    84, "Po", "Polonium",        "(209)",
    85, "At", "Astatine",        "(210)",
    86, "Rn", "Radon",           "(222)",
    87, "Fr", "Francium",        "(223)",
    88, "Ra", "Radium",          "226.0254",
    89, "Ac", "Actinium",        "(227)",
    90, "Th", "Thorium",         "232.0381",
    91, "Pa", "Protactinium",    "231.0359",
    92, "U" , "Uranium",         "238.029",
    93, "Np", "Neptunium",       "237.0482",
    94, "Pu", "Plutonium",       "(244)",
    95, "Am", "Americium",       "(243)",
    96, "Cm", "Curium",          "(247)",
    97, "Bk", "Berkelium",       "(247)",
    98, "Cf", "Californium",     "(251)",
    99, "Es", "Einsteinium",     "(254)",
   100, "Fm", "Fermium",         "(257)",
   101, "Md", "Mendelevium",     "(258)",
   102, "No", "Nobelium",        "(255)",
   103, "Lr", "Lawrencium",      "(260)",
   104, "Unq","Unq",             "(261)",
   105, "Unp","Unp",             "(262)",
   106, "Unh","Unh",             "(263)",
   107, "Uns","Uns",             "(262)",
   108, "Uno","Uno",             "?.?",
   109, "Une","Une",             "?.?",
};
#endif  // __PT_H__






[LISTING TWO]


//----- PT.CPP - Periodic Table for Windows -------
#include <afxwin.h>
#include <stdlib.h>
#include <math.h>
#include <string.h>

#include "resource.h"
#include "pt.h"

BOOL CPTDialog::bRegistered = FALSE;

//------ CPTDialog -- Registers our class if necessary, constructs the
//                    dialog object, and populates the combo boxes.
CPTDialog::CPTDialog()
{
    int i;
    if (!bRegistered)
        bRegistered = RegisterPTClass();
    Create ("pt");
    for (i=1; i<=PT_MAXELEMENTS; ++i)
    {
        CWnd::SendDlgItemMessage (IDD_ELEMENTNAME, CB_ADDSTRING,
                            0, (LONG)(LPSTR)_atom[i].element);
        CWnd::SendDlgItemMessage (IDD_ELEMENTSYMBOL, CB_ADDSTRING,
                            0, (LONG)(LPSTR)_atom[i].symbol);
    }
    // No combo-box has focus.
    memset (CB_Focus, 0, sizeof (CB_Focus));
}
//----- RegisterPTClass -- Register PT dialog class and replace the default
//      white box icon with our own.
//      Returns: nonzero if class is registered. 0 if class is not registered.
BOOL CPTDialog::RegisterPTClass()
{
    WNDCLASS wndclass;
    wndclass.style = 0;
    wndclass.lpfnWndProc = DefDlgProc;  /* use default dialog proc */
    wndclass.cbClsExtra = 0;
    // This field MUST be set to DLGWINDOWEXTRA, or this class we're
    // registering won't work properly with our dialog boxes.
    // MUST be set to DLGWINDOWEXTRA or class won't work properly
    wndclass.cbWndExtra = DLGWINDOWEXTRA ;
    // Use MFC global function to retrieve app's instance.
    wndclass.hInstance = AfxGetInstanceHandle();
    // Load custom icon, cursor, set background, load menu
    wndclass.hIcon = ::LoadIcon (AfxGetInstanceHandle(), "PTICON");
    wndclass.hCursor = ::LoadCursor (NULL, IDC_ARROW) ;
    wndclass.hbrBackground  = COLOR_WINDOW + 1 ;
    wndclass.lpszMenuName   = "PTMenu";
    // Need unique name here.  Name must be used in dialog box
    // template in DIALOGS.DLG to force dialog box to use this class.
    wndclass.lpszClassName  = "PTDLGCLASS";
    return ::RegisterClass(&wndclass);
}
//--- GetPTData -- Retrieve and display data based on location of input focus:
//  name, symbol, atomic number, or atomic weight. Accepts control ID as input.
void CPTDialog::GetPTData (int nID)
{
    char buffer[20];
    char *pWeight;
    double fBuffer;
    int i, bytes;
    bytes = CWnd::GetDlgItemText (nID, (LPSTR)buffer, sizeof(buffer));
    if (bytes > 0)
    {
        switch (nID)
        {
        case IDD_ELEMENTNAME:
            for (i=1; i<=PT_MAXELEMENTS; ++i)
            {
                if (_stricmp (buffer, _atom[i].element) == 0)
                    break;
            }
            break;
        case IDD_ELEMENTSYMBOL:
            for (i=1; i<=PT_MAXELEMENTS; ++i)
            {
                if (_stricmp (buffer, _atom[i].symbol) == 0)
                    break;
            }
            break;
        case IDD_ATOMICNUMBER:
            i = atoi (buffer);
            break;
        case IDD_ATOMICWEIGHT:
            //  Atomic weights stored as strings.  Convert
            //  to floating point to do comparisons.
            fBuffer = atof (buffer);
            for (i=1; i<=PT_MAXELEMENTS; ++i)
            {
                //  Some weights are in parens.
                //  Skip over them if detected.
                if (_atom[i].weight[0] != '(')
                    pWeight = _atom[i].weight;
                else
                    pWeight = &_atom[i].weight[1];

                if (atof (pWeight) >= fBuffer)
                    break;
            }
            break;
        default:
            i = 0;
            break;
        }
        //  i contains the index into the array of structures: _atom[i].xxxx
        if (i <= PT_MAXELEMENTS && i > 0)
            Display (i+100);
        else
            MessageBox ("Selected element not found in periodic table.",
                        "Error",
                        MB_OK | MB_ICONEXCLAMATION);
    }
}
//  OnNameSetFocus -- OnSymbolSetFocus -- OnNumberSetFocus -- OnWeightSetFocus
//    These functions keep track of which field has input focus. Fields are
//    mapped to array CB_Focus[]. If user clicks on any edit fields or type
//    in any data, blank out any existing data with Display (100).
void CPTDialog::OnNameSetFocus()
{
    Display (100);
    memset (CB_Focus, 0, sizeof(CB_Focus));
    CB_Focus[0] = 1;
}
void CPTDialog::OnSymbolSetFocus()
{
    Display (100);
    memset (CB_Focus, 0, sizeof(CB_Focus));
    CB_Focus[1] = 1;
}
void CPTDialog::OnNumberSetFocus()
{
    Display (100);
    memset (CB_Focus, 0, sizeof(CB_Focus));
    CB_Focus[2] = 1;
}
void CPTDialog::OnWeightSetFocus()
{
    Display (100);
    memset (CB_Focus, 0, sizeof(CB_Focus));
    CB_Focus[3] = 1;
}
//  OnCancel -- User pressed the "Cancel" button.  Terminate program.
void CPTDialog::OnCancel()
{
    delete this;
}
//  OnOK -- User pressed the "OK" button.  Retrieve element info
//          based on the edit box which has the input focus.
void CPTDialog::OnOK()
{
    //  CB_Focus tracks which edit box user entered data.  GetPTData
    //  searches for that data.
    if (CB_Focus[0])
        GetPTData (IDD_ELEMENTNAME);
    else if (CB_Focus[1])
        GetPTData (IDD_ELEMENTSYMBOL);
    else if (CB_Focus[2])
        GetPTData (IDD_ATOMICNUMBER);
    else
        GetPTData (IDD_ATOMICWEIGHT);
}
//  OnAbout -- User selected "About" from menu.  Open a modal dialog
//      box and tell user about this program.
void CPTDialog::OnAbout()
{
   CModalDialog about( "AboutBox", this );
   about.DoModal();
}
//  OnHelp -- User selected help from the menu.  Open a modal dialog
//      box and display help info.
void CPTDialog::OnHelp()
{
    CModalDialog version ("HelpBox", this);
    version.DoModal();
}
//  Display -- Display element info in the edit boxes using "SetDlgItemText"
void CPTDialog::Display(int iAtomicNumber)
{
    char szNumber[4];
   // Atomic numbers range from 1-108; to avoid conflict with IDOK and IDCANCEL
   // (1 & 2), 100 is added to atomic numbers. Adjust before indexing array.
    iAtomicNumber -= 100;
    //  Convert atomic number from numeric to string.
    if (iAtomicNumber <= 0)                        /* too small */
        memset (szNumber, 0, sizeof (szNumber));
    else if (iAtomicNumber > PT_MAXELEMENTS)       /* too big */
        sprintf (szNumber, "%3d", PT_MAXELEMENTS);
    else                                           /* juuust right */
        sprintf (szNumber, "%3d", _atom [iAtomicNumber].number);
    CWnd::SetDlgItemText (IDD_ELEMENTNAME, _atom [iAtomicNumber].element);
    CWnd::SetDlgItemText (IDD_ELEMENTSYMBOL, _atom [iAtomicNumber].symbol);
    CWnd::SetDlgItemText (IDD_ATOMICNUMBER, szNumber);
    CWnd::SetDlgItemText (IDD_ATOMICWEIGHT, _atom [iAtomicNumber].weight);
}
//    MESSAGE MAP
BEGIN_MESSAGE_MAP (CPTDialog, CDialog)
    ON_WM_CLOSE ()
    ON_COMMAND (IDM_ABOUT, OnAbout)
    ON_COMMAND (IDM_HELP, OnHelp)
    ON_COMMAND (IDCANCEL, OnCancel)
    ON_COMMAND (IDOK, OnOK)
    ON_CBN_SETFOCUS  (IDD_ELEMENTNAME, OnNameSetFocus)
    ON_CBN_SETFOCUS  (IDD_ELEMENTSYMBOL, OnSymbolSetFocus)
    ON_EN_SETFOCUS  (IDD_ATOMICNUMBER, OnNumberSetFocus)
    ON_EN_SETFOCUS  (IDD_ATOMICWEIGHT, OnWeightSetFocus)
    ON_COMMAND (H, OnH)
    ON_COMMAND (He, OnHe)
    ON_COMMAND (Li, OnLi)
    ON_COMMAND (Be, OnBe)
    ON_COMMAND (B, OnB)
    ON_COMMAND (C, OnC)
    ON_COMMAND (N, OnN)
    ON_COMMAND (O, OnO)
    ON_COMMAND (F, OnF)
    ON_COMMAND (Ne,OnNe)
    ON_COMMAND (Na, OnNa)
    ON_COMMAND (Mg, OnMg)
    ON_COMMAND (Al, OnAl)
    ON_COMMAND (Si, OnSi)
    ON_COMMAND (P, OnP)
    ON_COMMAND (S, OnS)
    ON_COMMAND (Cl, OnCl)
    ON_COMMAND (Ar, OnAr)
    ON_COMMAND (K, OnK)
    ON_COMMAND (Ca, OnCa)
    ON_COMMAND (Sc, OnSc)
    ON_COMMAND (Ti, OnTi)
    ON_COMMAND (V, OnV)
    ON_COMMAND (Cr, OnCr)
    ON_COMMAND (Mn, OnMn)
    ON_COMMAND (Fe, OnFe)
    ON_COMMAND (Co, OnCo)
    ON_COMMAND (Ni, OnNi)
    ON_COMMAND (Cu, OnCu)
    ON_COMMAND (Zn, OnZn)
    ON_COMMAND (Ga, OnGa)
    ON_COMMAND (Ge, OnGe)
    ON_COMMAND (As, OnAs)
    ON_COMMAND (Se, OnSe)
    ON_COMMAND (Br, OnBr)
    ON_COMMAND (Kr, OnKr)
    ON_COMMAND (Rb, OnRb)
    ON_COMMAND (Sr, OnSr)
    ON_COMMAND (Y, OnY)
    ON_COMMAND (Zr, OnZr)
    ON_COMMAND (Nb, OnNb)
    ON_COMMAND (Mo, OnMo)
    ON_COMMAND (Tc, OnTc)
    ON_COMMAND (Ru, OnRu)
    ON_COMMAND (Rh, OnRh)
    ON_COMMAND (Pd, OnPd)
    ON_COMMAND (Ag, OnAg)
    ON_COMMAND (Cd, OnCd)
    ON_COMMAND (In, OnIn)
    ON_COMMAND (Sn, OnSn)
    ON_COMMAND (Sb, OnSb)
    ON_COMMAND (Te, OnTe)
    ON_COMMAND (I, OnI)
    ON_COMMAND (Xe, OnXe)
    ON_COMMAND (Cs, OnCs)
    ON_COMMAND (Ba, OnBa)
    ON_COMMAND (La, OnLa)
    ON_COMMAND (Ce, OnCe)
    ON_COMMAND (Pr, OnPr)
    ON_COMMAND (Nd, OnNd)
    ON_COMMAND (Pm, OnPm)
    ON_COMMAND (Sm, OnSm)
    ON_COMMAND (Eu, OnEu)
    ON_COMMAND (Gd, OnGd)
    ON_COMMAND (Tb, OnTb)
    ON_COMMAND (Dy, OnDy)
    ON_COMMAND (Ho, OnHo)
    ON_COMMAND (Er, OnEr)
    ON_COMMAND (Tm, OnTm)
    ON_COMMAND (Yb, OnYb)
    ON_COMMAND (Lu, OnLu)
    ON_COMMAND (Hf, OnHf)
    ON_COMMAND (Ta, OnTa)
    ON_COMMAND (W, OnW)
    ON_COMMAND (Re, OnRe)
    ON_COMMAND (Os, OnOs)
    ON_COMMAND (Ir, OnIr)
    ON_COMMAND (Pt, OnPt)
    ON_COMMAND (Au, OnAu)
    ON_COMMAND (Hg, OnHg)
    ON_COMMAND (Tl, OnTl)
    ON_COMMAND (Pb, OnPb)
    ON_COMMAND (Bi, OnBi)
    ON_COMMAND (Po, OnPo)
    ON_COMMAND (At, OnAt)
    ON_COMMAND (Rn, OnRn)
    ON_COMMAND (Fr, OnFr)
    ON_COMMAND (Ra, OnRa)
    ON_COMMAND (Ac, OnAc)
    ON_COMMAND (Th, OnTh)
    ON_COMMAND (Pa, OnPa)
    ON_COMMAND (U, OnU)
    ON_COMMAND (Np, OnNp)
    ON_COMMAND (Pu, OnPu)
    ON_COMMAND (Am, OnAm)
    ON_COMMAND (Cm, OnCm)
    ON_COMMAND (Bk, OnBk)
    ON_COMMAND (Cf, OnCf)
    ON_COMMAND (Es, OnEs)
    ON_COMMAND (Fm, OnFm)
    ON_COMMAND (Md, OnMd)
    ON_COMMAND (No, OnNo)
    ON_COMMAND (Lr, OnLr)
    ON_COMMAND (Unq, OnUnq)
    ON_COMMAND (Unp, OnUnp)
    ON_COMMAND (Unh, OnUnh)
    ON_COMMAND (Uns, OnUns)
    ON_COMMAND (Uno, OnUno)
    ON_COMMAND (Une, OnUne)
END_MESSAGE_MAP()

//  Create the application object
CTheApp theApp;
//  InitInstance -- Make CPTDialog object the main window by assigning
//      m_pMainWnd to constructor.  Returns:    TRUE
BOOL CTheApp::InitInstance()
{
   m_pMainWnd = new CPTDialog();
   m_pMainWnd->ShowWindow( m_nCmdShow );
   m_pMainWnd->UpdateWindow();

   return TRUE;
}





[LISTING THREE]


//----- PT.RC - Resources for Periodic Table -----
#include <windows.h>
#include <afxres.h>
#include "resource.h"

PTICON      ICON    pt.ico
PTMenu MENU
{
    POPUP       "&Help"
    {
        MENUITEM "&General Information", IDM_HELP
        MENUITEM "&About Periodic Table", IDM_ABOUT
    }
}
rcinclude dialogs.dlg        // file generated by MS Dialog Editor
//------- End of PT.RC -------



Copyright © 1993, Dr. Dobb's Journal

Terms of Service | Privacy Statement | Copyright © 2024 UBM Tech, All rights reserved.