Dr. Dobb's is part of the Informa Tech Division of Informa PLC

This site is operated by a business or businesses owned by Informa PLC and all copyright resides with them. Informa PLC's registered office is 5 Howick Place, London SW1P 1WG. Registered in England and Wales. Number 8860726.


Channels ▼
RSS

C/C++

C Programming


July 1996: C Programming

Hairy Arms for Dummies, CD-ROM Setups, and Java Jive

Al Stevens

Back in my mainframe days, I was assigned to an Army post on a project developing a mobile personnel system to keep track of troops in the field. Unpopular wars were as popular then as they are now, and the military-industrial complex was, indeed, complex. Working with the military always involves reports and meetings. Developers write formal reports describing every stage of analysis, design, and development. The brass respond to the reports with written questions. The developers write memos to answer the questions. And everyone attends meetings to discuss the memos, out of which evolve requirements for more reports. This endless cycle is, of course, absolutely necessary for the development of a quality system, as you would know if you associated with people who always try to look and talk like John Wayne.

Now, it is important to understand that all involved middle-management brass have to compose and submit at least one question to each report, preferably a question that results in a change to any subsequent report. They must leave their mark on whatever they touch, preferably a permanent paper mark, because they were constantly in quest of that next elusive promotion.

Thus the hairy arm. It was our practice to intentionally insert unimportant material that could be addressed, discussed, and changed so that the toy soldiers would have something to do. We called these insertions "hairy arms," and they were meant to divert the brass from the details and keep them from breaking something important. This is a valuable lesson. Whenever you write a report to be reviewed and corrected by bureaucrats, include at least one hairy arm for each reviewer.

It is a game. They know that you are doing it, you know that they know that you are doing it. They know that you know that they know that you are doing it (although you might not know that they know, et cetera), everyone knows why you are doing it, and everyone is happy.

It is also an art. You have to evenly distribute the hairy arms according to the skills and knowledge of your audience so that everyone gets something to change.

One such report on our project came up short in the hairy arm department. The report projected the amount of space that each disk file in the personnel system would require. Down came the usual barrage of formal questions addressing the hairy arms, but one reviewer, low on the totem, newly arrived, and without a hairy arm of his very own, had to contrive something. Fortunately, he was smart enough to stay out of the morass. Everyone must pretend that the questions are important, and they must be seriously addressed, so he asked the important-sounding but unimportant question, "Why do your estimates not include disk space requirements for the Command Personnel File?"

The project team convened an informal meeting to discuss answers to this latest round of questions. The answer to the unexpected question was, simply, "The report does not estimate disk space requirements for the Command Personnel File because that file is maintained on magnetic tape." Amidst muffled titters about this uninformed questioner, my pal Barney scrawled the word "dummies" in the draft response's margin following the answer to the question. More laughter. The meeting adjourned, and the draft was typed, signed, and delivered.

The obligatory meeting with the brass ensued. One by one they went over the questions and answers. The question in question was last on the agenda, and the answer should have been self-explanatory, but the reigning bird colonel surprised everyone by saying, "Harumph. Now, about this last item..." Hearts froze, faces reddened, and a cold realization set in as the colonel continued, "...what exactly are magnetic tape dummies?"

Of course. The typist had interpreted Barney's sarcastic pejorative as an editorial change and added it to the sentence. No one had noticed the small, insignificant addition on the final copy before sending it out.

My boss Bill gulped, stammered, lit a cigarette, took a long drink of coffee (obvious and drastic delaying tactics, since Bill is a Mormon), and then blurted out a brilliant ad lib about how blank tapes are prepared with a dummy header label before being used, thus the moniker. The colonel was satisfied, the meeting adjourned, and the team returned to their hovels to write more design, more code, and more reports with a firm resolve to screen all memos more diligently thereafter and evermore.

Months later, the project team demonstrated a simulation at a data center of the new field personnel system for the benefit of the brass, who beamed proudly at each feature because they could once again take credit for a job well done. Team members acted as surrogate field operators, typing on the console, flipping switches, hanging tapes, and mounting disk packs as Bill narrated, "...and over here we have simulated the Command Personnel File on magnetic tape dummies." No one was able to look one another in the eye for the duration of the presentation.

Alabamy Bound?

I used to get a lot of attention from Borland. I used Borland C++ to develop all my DOS projects, and loved it. Since I starting working on Windows 95 projects, I've been using Visual C++ and stating my preference for MFC over OWL. As a consequence, the folks at Borland have forgotten my name. It was bound to happen. I received a flyer in the mail today promoting the 1996 Borland Developer's Convention to be held in Anaheim, on July 27-31. It was addressed to Alabama Stevens, Dr. Dobb's Journal.

Wanted: A Setup in Drag

My CD-ROM project continues. I am developing a programming tutorial CD-ROM in partnership with Dr. Dobb's, and, in observation of the traditional Pournellean Imperative, I get to shamelessly plug my own stuff at every turn in this column. Look for ads for the CD-ROM somewhere in this and later issues. This unabashed self-aggrandizing is not as mercenary as it might seem. Some readers of this column are not likely to need what the CD- ROM delivers, which are lessons on programming in Qbasic, C, and C++. Also, many of you will benefit from these reports of the bumps and ruts that I hit along the way, so it is fitting that I discuss these issues here. Therefore it is a natural side effect that the product be plugged, er, identified.

The CD-ROM product includes three interactive tutorials that I built using Asymetrix Toolbook Multimedia. The tutorials launch Quincy 96, described in recent editions of this column, a Windows 95-hosted IDE front end for the gnu-win32 port of the GNU C and C++ compilers. The tutorials also launch Herman, a text viewer that I discussed last month, to display the text of the three books that provide the lesson content.

I designed the interface between the tutorials and the supporting programs to be position independent within the file system, which means that, given the subdirectory of the tutorial's executable file, the programs can find one another and their data files. This strategy eliminates external changes to config.sys, autoexec.bat, INI files, registry entries, environment variables, or anything that would need to be established or changed as a function of the installation. My objective was to have a totally nonintrusive installation, one that installs a system that runs without modifying system files, doesn't need a reboot, and can be uninstalled simply by the deletion of its subdirectories.

Having achieved that objective, I realized that the system did not seem to need a Setup.exe program. To install it, you would merely drag a folder from the CD-ROM to the desktop or disk drive of the target computer. Running the program would consist of double-clicking its icon in the target copy of the folder. You could uninstall it by dragging that folder to the Recycle Bin. What a concept! This might catch on. This could become a standard. I could get an award, be granted a patent, be invited to appear on John C. Dvorak's radio talk show, or something really impressive like that. I set it up and gave it a try. It worked.

Because I was using a Zip drive to emulate the CD-ROM, that nagging worry-wart from last month who stays on my shoulder and insists on attention to detail (he arrived long after my military involvement was concluded) urged me to burn a test CD-ROM and verify the procedure. I gave in and wasted a blank. Sure enough the procedure failed. Bless that little shoulder percher. For some reason, files dragged from a CD-ROM retain the read-only attribute, which exists only because of the file's medium. The drag installation operation delivered a tutorial that could not keep track of a student's progress, would not allow the student to modify and recompile any of the exercise programs, and refused to let anything be deleted without a lot of bother. Bummer. How does Microsoft always manage to anticipate my every need only to defeat it with arbitrary design decisions?

A setup program is necessary after all, and that program is the technical focus of this month's column. To comply with the de facto standards of Windows 95 applications, a setup program should be named Setup.exe, be easy for the user to find on the distribution medium, let the user identify destination drives and subdirectories, ensure that the target drive has enough room for the files, copy the files, let the user abort the process at any time, and, upon successful completion, add an appropriate entry to the Start menu or a subordinate menu to launch the application.

Asymetrix Toolbook includes a setup wizard that builds such a setup program. It assumes that the source files are to be compressed on diskettes, which mine are not. The Win32 SDK includes a setup toolkit, which allows you to build a script-driven setup procedure and a C program to run the setup procedure. I read all the documentation and was overwhelmed by the level of complexity of this particular approach. Okay, before you make a mad dash to send me some e-mail, I'll concede that the process is probably simple once you understand it. Nonetheless, I concluded that it would take less time to build a dialog-based Visual C++ MFC 4.0 Setup application than it would take to climb the Setup SDK's learning curve.

Listings One and Two are setup.h and setup.cpp, respectively. These files implement the derived application class for the dialog-based setup program. The application class overrides the CWinApp::InitInstance function and supplies a CString data member into which the program's executable file subdirectory is stored. You can determine the subdirectory of an MFC program's executable file from the CWinApp object's m_pszHelpFilePath data member, which records the path and filename of the help file, located in the same subdirectory as the executable file. Setup uses this path to find the files to install and to ensure that the user is installing from a CD-ROM drive. This strategy prevents would-be pirates from copying the CD-ROM's contents to a network drive and allowing everyone to install the tutorial onto their workstations. By intercepting the practice from the install program, I can save them some time. The tutorial works only if the student has a CD-ROM drive and has my CD-ROM disc inserted in it. This measure is necessary because a lot of the multimedia stuff is not copied during installation. It would consume far too much hard disk space. Many multimedia applications today use similar techniques.

The overridden CWinApp::InitInstance function checks to see if the user has a file named cygwin.dll in the Windows system subdirectory. If so, there is a very good chance that Quincy 96 will be unable to compile and execute exercise programs. The gnu-win32 port of the GNU C/C++ compilers uses the DLL to implement the run-time library. Both the compiler programs and the executables they compile require that DLL. The gnu-win32 port is going through many versions during its development, and there is no upward or downward compatibility between versions of the DLL. Therefore, Setup searches to see if the system has another copy of the DLL in the Windows subdirectory that would override the one being installed. If so, Setup reports that situation and offers to rename the old file for the user. This is unfortunate but necessary. If, by coincidence, the user has installed other programs compiled with gnu-win32, those programs need their particular version of cygwin.dll. But Quincy 96 installs a compiler that requires a specific version, which would probably conflict with the other one. It becomes necessary to disable the old (or, perhaps, newer) DLL to enable the tutorial to work. Of course, there is little that I can do if a user installs my tutorial and then installs that other gnu-win32 application later.

Listings Three and Four are setupdlg.h and setupdlg.cpp, the source-code files that implement the Setup program's dialog box. The header file contains two constants that identify the number of files that Setup copies and the disk space required. I'll change those numbers when the tutorial is complete. The Setup program uses the disk-space constant to see if there is enough room on the hard disk for the installation, and the file-count constant to manage the display of a CProgressCtrl that indicates the progress of the copying procedure.

The CSetupDlg::OnInstall function starts the installation procedure after the user selects an install path for the tutorial and clicks the Install button. The function validates the target path, computes and validates the available disk space, and alerts the user if they are installing to the root directory of their hard drive. If the target subdirectory exists, the program asks if the user intends to use an existing subdirectory. After everything is correct, the function launches a Win32 thread to do the copying. Using a different thread allows the GUI to maintain control in case the user presses the Cancel button during the copies. The thread copies all the files and subdirectories from the DDJTUTOR subdirectory (under the setup program's executable-file subdirectory) to the target subdirectory. The thread sends a WM_COMMAND/IDC_SETFILENAME message to the dialog box for each new file so that the dialog can display the filename being copied.

Figure 1 shows the Setup program being run. The resource source-code files are included with the source code that you can download.

Java versus C++

Bjarne Stroustrup says that if he had decided to build a new language instead of launching one that maintained C compatibility, his creation might have been somewhat better than C++ but would have remained an "unimportant cult language." It has been suggested that Java is the better language that Dr. Stroustrup would have designed if he had been free from the bounds of C compatibility. I am no Java expert, but I tend to doubt it. There are significant differences between what the two languages support.

Java has no preprocessor, a C feature that detractors, Stroustrup among them, commonly target. Java claims to have no pointers, but that claim is inaccurate, as I'll explain later. Java also eliminates some C++ features that C++ detractors attack: overloaded operators, multiple inheritance, friends, and global declarations made outside of class definitions. Java lacks templates, and, consequently, anything resembling the generic programming model of STL, but a facility to support generic classes is promised to be available sometime soon. It remains to be seen how a language without pointer arithmetic will support the STL iterator concept when applied to containment of intrinsic types. Java also seems to be missing other important C/C++ features, such as destructors, unions, unsigned integers, volatile-data members, implicit-conversion constructors, and so on.

So how come Java did not remain an unimportant cult language as Stroustrup predicted? One answer is that a Java subset called "JavaScript" has become the accepted language for implementing executable content in Web pages. Web browsers with built-in JavaScript interpreters can execute so-called applets embedded in the HTML of Web pages, and the execution occurs in the browser client computer rather than at the remote server site. The potential for interesting Web pages rises dramatically when the page can do more than simply display itself and provide hot links to other pages. When people project that the Internet is the operating system of tomorrow, this is some of what they mean.

Java, the language (as opposed to JavaScript), is interesting to C and C++ application programmers, and therefore readers of this column, because Java looks like a C++ knockoff. The syntax, structure, and programming model of the two languages are sufficiently similar that C++ programmers easily understand Java code. What is more, C programmers might have less difficulty making the transition to Java than they do to C++, primarily due to what was left out of Java.

The Java language is capable of supporting full application development beyond the limited realm of Web page applets. Its Abstract Window Toolkit is a platform-independent graphical-user-

interface class library. What is needed are some of the same developer tools that C++ programmers currently enjoy: visual composition programs, component objects, frameworks, and so on. I missed SD '96 in San Francisco this year, but I hear that Java was everywhere.

Do not assume from these comments that I have become a Java convert or expert. I owe all my Java knowledge to a close reading of one of the many recent books on the subject, Java for C/C++ Programmers, by Michael Daconta (John Wiley & Sons, 1996). I will dwell here on some criticisms of the book's discussions of C and C++. On the whole, I found the book to be thorough and enjoyable, but it exhibits a characteristic that has annoyed me ever since the first C++ books began to appear. When something is an improvement to something else, even if only in the view of the authors, those authors tend to deprecate that which is being superseded. Such criticisms often spring out of a writer's need to justify something that is different. If it weren't better it wouldn't need to be different, or so the argument goes. Often the writer just gets it wrong.

For example, Daconta says that Java's elimination of variable arguments was because Java's Vector class made them unnecessary. This does not appear to be the case. I believe that variable arguments went away only because they are typically implemented with macros, which are a function of the much-maligned C preprocessor, which has been eliminated in Java. I don't mind that Java has neither a preprocessor nor variable arguments, but I do not see how Java's Vector class replaces C's variable arguments, which allow the called function to determine at run time the types of its arguments based on data found in an anchor argument. You might argue that the absence of type-safety in such a scheme is inherently lame, and you might be right, but the Vector class does not replace the feature.

Daconta also misunderstands polymorphism, from which I infer that he is primarily a C programmer converted to Java. He asserts that C++ and Java support polymorphism with overloaded member functions, and does not mention polymorphism in his discussion of virtual functions. His discussion of virtual functions demonstrates that he does, however, understand their behavior even if he does not understand the object-oriented concept that they implement.

I was surprised to find my name in the book. In a discussion of what the author thinks is wrong with standard C++, readers are referred to my January 1996 column. While I applaud the author's taste in columns to read and columnists with whom to associate, I must object to the context of this particular citation. The implication is that I agree with the author's argument that C++ multiple inheritance and references are bad language features. In the first place, that column had nothing to do with multiple inheritance and references. In the second place, I actually like references and will argue with anyone who says that valid applications for multiple inheritance don't exist.

It is not surprising that Daconta gets C++ references wrong. He does not understand them, as this statement shows: "The reference is a feature that has two purposes: first to eliminate the need for pointers, and second, to make operator overloading work." This odd assertion must be intended to support the "different is better" position he takes on Java. Java has no pointer arithmetic and no overloaded operators, so leaving out references makes sense if their only purpose is to support features that were themselves left out. That works if his conclusions about references are right, which they are not. He goes on to presume to know why Dr. Stroustrup added references to the language: "Actually, the fact that references exist in C++ is a statement that the language designer was attempting to band-aid a flaw instead of eliminating it." That statement teeters somewhere between the presumptuous and the preposterous.

All that notwithstanding, Daconta's description of Java tells me that references and pointers are in fact alive and well in Java. He calls them "pointer pointers." Two characteristics distinguish Java references from C++ references. First, a Java reference does not need to be initialized; such a reference has a null value like a C pointer and is called an "uninstantiated object." Second, a Java reference can be assigned to, which means that it refers to different objects at different times, also like a C pointer. All of which merges pointers and references into one hybrid reference/ pointer data type. You cannot perform pointer arithmetic, which might compromise the notion of STL-like iterators for built-in types when generics are added, and Java is free from C's incestuous relationship between pointers and arrays, which is the best thing they've changed, I think. So, from my perspective, Java has a working pointer/reference mechanism, no matter what they call it.

I'm not convinced that Java has no preprocessor either. CPP is, after all, just a text-to-text program that reads source code, acts upon a set of preprocessor directives, and writes modified source code to be passed on to the translator. I see nothing about Java to prevent anyone from using CPP as a preprocessor to include files, implement macros, and execute compile-time conditionals. You can argue the merits and vagaries of CPP all you want. It's there, and programmers being programmers, it will be used, particularly since Java provides no support for the equivalent of assert.h. Daconta dismisses the value of the Standard C assert macro, but his argument that "data members of properly implemented objects are always in a stable state" is just silly. Assert is for debugging. It's for when your objects cannot be assumed to be properly implemented yet because the program does not work yet.

I like this book, but I am concerned that C programmers who do not know C++ will read the author's misinformed denigration of C++ and be misled about C++, which is clearly still the superior programming language. C++ programmers who read it will not be misled, of course.

When writers address their work to a culture of programmers, they should set out first to understand that culture. This particular work is aimed at us C and C++ programmers, and it does not get high marks in that area. The target audience's reaction is likely to range from amusement at the author's ignorance of our language, to resentment of his faint attempts to malign the features of these still dominant and valid Java ancestors. Sell Java on its own merits if you will, but stop trying to justify its features by comparing it to real and imagined flaws in C and C++.

Once you get past the C and C++ chapters, the book is very good. Daconta's treatment of Java is clear and concise, and, assuming that his descriptions are accurate, I think that I gained an entry-level understanding of Java from his work. Based on that, I recommend the book to anyone who wants to learn more about Java.

Farewell to Careware

For several years I have included a section every month in this column explaining how you can get the source code to my projects. Most readers who want the code can download it, as page three of each issue explains. I encouraged others to send a disk, onto which I could copy the code, and a stamped, addressed disk mailer. The code was always free, but I suggested that you include a careware buck that I would give to the Brevard Community Food Bank. Most readers sent that buck, and many of you sent more than a buck. Over the years, the careware program contributed a significant amount of money to the Food Bank. A small dent in a big problem, but each of you made a difference through your generosity. Technology overtakes events. More readers have online access now, and some of my projects exceed the capacity of a diskette. Consequently, the number of requests for code disks has dwindled to a trickle, and so I will discontinue the careware pleas. That's okay. Please continue to use my code in ways that make you happy and successful. But always remember that the human condition knows no greater tragedy than that of a child who routinely copes with hunger. As long as one such child lives on this planet, the rest of us are obligated to do what we can. So when something that we publish here helps you in your life or your work, think about those children. I'm not after Sally Struthers' gig, but I will remind you, just this one more time, that most communities have organizations that help the homeless and hungry. Please support them whenever and however you can.

Listing One

// --- setup.h
#include "resource.h"

class CSetupApp : public CWinApp
{
    CString m_strSourcePath;  // source path for install
public:
    CSetupApp() { }
    const CString& GetSourcePath() const
        { return m_strSourcePath; }
    virtual BOOL InitInstance();
};
extern CSetupApp theApp;

Listing Two


// setup.cpp
#include "stdafx.h"
#include <io.h>
#include "setup.h"
#include "setupDlg.h"

CSetupApp theApp;

BOOL CSetupApp::InitInstance()
{
    Enable3dControlsStatic();
    BOOL bPathOK = FALSE;
    // --- make sure they are installing from the root path of CD-ROM
    m_strSourcePath = m_pszHelpFilePath;
    int nIndex = m_strSourcePath.ReverseFind('\\');
    if (nIndex > 0)
        m_strSourcePath = m_strSourcePath.Left(nIndex+1);
    if (m_strSourcePath.GetLength() == 3)   {
        if (m_strSourcePath.Right(2) == ":\\")  {
            UINT nDriveType = GetDriveType(m_strSourcePath);
            if (nDriveType == DRIVE_CDROM)
                bPathOK = TRUE;
        }
    }
    if (!bPathOK)   {
        AfxMessageBox("Install from CD-ROM only", MB_ICONSTOP);
        return FALSE;
    }
    int len = 100;
    int dirlen;
    char* psysdir = 0;
    do  {
        dirlen = len;
        delete psysdir;
        psysdir = new char[dirlen+11];
        int len = GetSystemDirectory(psysdir, dirlen);
    } while (dirlen != len);
    char* pnewdll = new char[dirlen+11];
    strcpy(pnewdll, psysdir);
    strcat(psysdir, "\\cygwin.dll");
    strcat(pnewdll, "\\cygwin.sav");
    if (_access(psysdir, 0) == 0)   {
        CString msg(
            "Setup has found a file named cygwin.dll"
            " in the system directory.\n\n("
        );
        msg += CString(psysdir);
        msg += ")\n\n";
        msg +=
  "This DLL can, and probably will, cause Quincy to malfunction.\n"
  "Different versions of this DLL are incompatible with one another.\n"
  "You must rename or remove this file before you run the tutorial.\n"
  "Setup can rename the file to cygwin.sav if you wish.\n\n"
  "Should Setup rename the file now?";
        if (AfxMessageBox(msg, MB_YESNO | MB_ICONQUESTION) == IDYES)
            rename(psysdir, pnewdll);
    }
    delete [] pnewdll;

    delete [] psysdir;
    CSetupDlg dlg;
    m_pMainWnd = &dlg;
    dlg.DoModal();
    return FALSE;
}

Listing Three

// setupDlg.h

const LONG nDiskSpaceRequired = 40000; // space required in KB
const INT nFileCount = 1000;           // number of files to copy
class CSetupDlg : public CDialog
{
    static UINT StartCopies(void*);
    void CopyFiles();
    void CopyFiles(const CString& strDestination, const CString& strSource);
    BOOL m_bCopying;
    BOOL m_bAborting;
public:
    CSetupDlg(CWnd* pParent = NULL);
    //{{AFX_DATA(CSetupDlg)
    enum { IDD = IDD_SETUP_DIALOG };
    CButton m_butInstall;
    CProgressCtrl m_ctlProgress;
    CString m_strDoing;
    CString m_strPath;
protected:
    virtual void DoDataExchange(CDataExchange* pDX);
    virtual LRESULT WindowProc(UINT message,WPARAM wParam,LPARAM Param);
protected:
    HICON m_hIcon;
    virtual BOOL OnInitDialog();
    afx_msg void OnPaint();
    afx_msg HCURSOR OnQueryDragIcon();
    afx_msg void OnInstall();
    virtual void OnCancel();
    DECLARE_MESSAGE_MAP()
};

Listing Four

// --- setupDlg.cpp
#include "stdafx.h"
#include <direct.h>
#include <io.h>
#include <sys\stat.h>
#include "setup.h"
#include "setupDlg.h"

CSetupDlg::CSetupDlg(CWnd* pParent /*=NULL*/)
          : CDialog(CSetupDlg::IDD, pParent)
{
    m_strDoing = _T("");
    m_strPath = _T("");
    m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);
    m_bCopying = FALSE;
    m_bAborting = FALSE;
}
void CSetupDlg::DoDataExchange(CDataExchange* pDX)
{
    CDialog::DoDataExchange(pDX);
    DDX_Control(pDX, IDC_INSTALL, m_butInstall);
    DDX_Control(pDX, IDC_PROGRESS, m_ctlProgress);
    DDX_Text(pDX, IDC_DOING, m_strDoing);
    DDX_Text(pDX, IDC_PATH, m_strPath);
}
BEGIN_MESSAGE_MAP(CSetupDlg, CDialog)
    ON_WM_PAINT()
    ON_WM_QUERYDRAGICON()
    ON_BN_CLICKED(IDC_INSTALL, OnInstall)
END_MESSAGE_MAP()
BOOL CSetupDlg::OnInitDialog()
{
    CDialog::OnInitDialog();
    SetIcon(m_hIcon, TRUE);         // Set big icon
    SetIcon(m_hIcon, FALSE);        // Set small icon
    m_strPath = "C:\\DDJTUTOR";
    UpdateData(FALSE);
    return TRUE;
}
void CSetupDlg::OnPaint()
{
    if (IsIconic())
    {
        CPaintDC dc(this);
        SendMessage(WM_ICONERASEBKGND, (WPARAM) dc.GetSafeHdc(), 0);
        int cxIcon = GetSystemMetrics(SM_CXICON);
        int cyIcon = GetSystemMetrics(SM_CYICON);
        CRect rect;
        GetClientRect(&rect);
        int x = (rect.Width() - cxIcon + 1) / 2;
        int y = (rect.Height() - cyIcon + 1) / 2;
        dc.DrawIcon(x, y, m_hIcon);
    }
    else
        CDialog::OnPaint();
}
HCURSOR CSetupDlg::OnQueryDragIcon()
{
    return (HCURSOR) m_hIcon;
}
void CSetupDlg::OnInstall()
{
    UpdateData(TRUE);
    // ----- edit path information
    char drive = toupper(m_strPath.GetAt(0));
    char path[] = "c:\\";
    path[0] = drive;

    if (drive < 'A' || drive > 'Z' ||
            m_strPath[1] != ':' || m_strPath[2] != '\\')  {
        AfxMessageBox(
   "Specify target drive and directory (e.g. C:\\DDJTUTOR)", MB_ICONSTOP);
        return;
    }
    UINT nDriveType = GetDriveType(path);
    if (nDriveType != DRIVE_REMOVABLE && nDriveType != DRIVE_FIXED) {
        AfxMessageBox("Install to local hard disk only", MB_ICONSTOP);
        return;
    }
    // ----- test for available disk space
    m_strDoing = "Checking for available disk space";
    UpdateData(FALSE);
    DWORD nSectorsPerCluster;
    DWORD nBytesPerSector;
    DWORD nNumberOfFreeClusters;
    DWORD nTotalNumberOfClusters;
    GetDiskFreeSpace(path, &nSectorsPerCluster, &nBytesPerSector,
                           &nNumberOfFreeClusters, &nTotalNumberOfClusters);
    LONG space =
       (nNumberOfFreeClusters * (nSectorsPerCluster * nBytesPerSector)) / 1024;
    if (space < nDiskSpaceRequired)  {
        CString msg;
        msg.Format(
           "Tutorial requires %ldKB\n"
           "%c: has only %ldKB", nDiskSpaceRequired, *path, space);
        AfxMessageBox(msg, MB_ICONSTOP);
        m_strDoing = "";
        UpdateData(FALSE);
        return;
    }
    // ---- enough space, verify installation request
    CString strMsg("Install to ");
    strMsg += m_strPath + "?";
    if (AfxMessageBox(strMsg, MB_YESNO | MB_ICONQUESTION) != IDYES){
        m_strDoing = "";
        UpdateData(FALSE);
        return;
    }
    // ---- if path ends with \ delete it
    if (m_strPath.Right(1) == "\\") {
        if (m_strPath.Right(2) == ":\\")    {
            // ---- discourage installation to the root directory
            if (AfxMessageBox(
              "Do you really want to install into the root directory?",
               MB_ICONQUESTION) == IDNO)
                return;
        }
        else
            m_strPath = m_strPath.Left(m_strPath.GetLength()-1);
    }
    // ----- test to see if installation subdirectory exists
    if (_chdir(m_strPath) == 0)  {
        CString msg(m_strPath);
        msg += " already exists.\nUse it?";
        if (AfxMessageBox(msg, MB_YESNO | MB_ICONQUESTION) != IDYES) {
            m_strDoing = "";
            UpdateData(FALSE);
            return;
        }
    }
    m_butInstall.EnableWindow(FALSE);
    m_ctlProgress.SetRange(0, nFileCount);
    m_ctlProgress.SetStep(1);
    m_bCopying = TRUE;
    m_strDoing = "Copying:";
    UpdateData(FALSE);
    AfxBeginThread(&CSetupDlg::StartCopies, this);
}
UINT CSetupDlg::StartCopies(void* This)
{
    ((CSetupDlg*)This)->CopyFiles();
    return 0;
}
void CSetupDlg::CopyFiles()
{
    CopyFiles(m_strPath, theApp.GetSourcePath() + "DDJTUTOR");
    if (m_bCopying)
        AfxMessageBox("Setup completed");
    else
        AfxMessageBox(
            "Note: Setup did not run to completion.\n"
            "You should rerun Setup before trying\n"
            "to use the tutorial. Some files may\n"
            "have been copied before Setup cancelled\n"
            "the copying operation. Delete these files\n"
            "if you do not reinstall to the same\n"
            "drive and subdirectory.",
            MB_ICONSTOP);
    PostMessage(WM_COMMAND, IDOK, 0);
    m_bCopying = FALSE;
}
void CSetupDlg::CopyFiles(const CString& strDestination,   const CString& strSource)
{
    // ------ make the destination subdirectory
    if (_mkdir(strDestination) != 0)  {
        CString msg("Error! Cannot create ");
        msg += strDestination;
        AfxMessageBox(msg, MB_ICONSTOP);
        m_bCopying = FALSE;
        m_bAborting = TRUE;
        return;
    }
    // ------ scan for files to copy
    struct _finddata_t fileinfo;
    CString strSub = strSource + "\\*.*";
    long hFile = _findfirst(strSub.GetBuffer(0), &fileinfo);
    while (!m_bAborting && hFile != -1)  {
        if (!m_bCopying)  {
            // ---- pressed Cancel
            if (AfxMessageBox("Stop copying?",
                           MB_YESNO | MB_ICONQUESTION) == IDYES) {
                m_bAborting = TRUE;
                break;
            }
            // --- changed your mind
            m_bCopying = TRUE;
        }
        // ---- build destination file specification
        CString strDest(strDestination);
        strDest += "\\";
        strDest += fileinfo.name;
        // ---- build source file specification
        CString strSrc(strSource);
        strSrc += "\\";
        strSrc += fileinfo.name;
        if (fileinfo.attrib & _A_SUBDIR)    {
            // --- this is a subdirectory
            if (*fileinfo.name != '.')
                CopyFiles(strDest, strSrc);
        }
        else  {
            SendMessage(WM_COMMAND, IDC_SETFILENAME,
                       (LPARAM)strDest.GetBuffer(0));
            if (CopyFile(strSrc, strDest, FALSE) == FALSE)  {
                CString msg("Error! Unable to copy ");
                msg += strDest;
                AfxMessageBox(msg, MB_ICONSTOP);
                m_bCopying = FALSE;
                m_bAborting = TRUE;
                break;
            }
            m_ctlProgress.StepIt();
            // ----- turn off read-only attribute in destination file
            _chmod(strDest, _S_IWRITE);
        }
        if (_findnext(hFile, &fileinfo) == -1)
            hFile = -1;
    }
}
void CSetupDlg::OnCancel()
{
    if (m_bCopying)
        m_bCopying = FALSE;
    else
        CDialog::OnCancel();
}
LRESULT CSetupDlg::WindowProc(UINT message, WPARAM wParam, LPARAM lParam)
{
    if (message == WM_COMMAND && wParam == IDC_SETFILENAME) {
        m_strDoing = (char*)lParam;
        UpdateData(FALSE);
        return TRUE;
    }
    return CDialog::WindowProc(message, wParam, lParam);
}


Related Reading


More Insights






Currently we allow the following HTML tags in comments:

Single tags

These tags can be used alone and don't need an ending tag.

<br> Defines a single line break

<hr> Defines a horizontal line

Matching tags

These require an ending tag - e.g. <i>italic text</i>

<a> Defines an anchor

<b> Defines bold text

<big> Defines big text

<blockquote> Defines a long quotation

<caption> Defines a table caption

<cite> Defines a citation

<code> Defines computer code text

<em> Defines emphasized text

<fieldset> Defines a border around elements in a form

<h1> This is heading 1

<h2> This is heading 2

<h3> This is heading 3

<h4> This is heading 4

<h5> This is heading 5

<h6> This is heading 6

<i> Defines italic text

<p> Defines a paragraph

<pre> Defines preformatted text

<q> Defines a short quotation

<samp> Defines sample computer code text

<small> Defines small text

<span> Defines a section in a document

<s> Defines strikethrough text

<strike> Defines strikethrough text

<strong> Defines strong text

<sub> Defines subscripted text

<sup> Defines superscripted text

<u> Defines underlined text

Dr. Dobb's encourages readers to engage in spirited, healthy debate, including taking us to task. However, Dr. Dobb's moderates all comments posted to our site, and reserves the right to modify or remove any content that it determines to be derogatory, offensive, inflammatory, vulgar, irrelevant/off-topic, racist or obvious marketing or spam. Dr. Dobb's further reserves the right to disable the profile of any commenter participating in said activities.

 
Disqus Tips To upload an avatar photo, first complete your Disqus profile. | View the list of supported HTML tags you can use to style comments. | Please read our commenting policy.