C Programming

Al hears from you on topics ranging from Setup versus Uninstall programs, Registry versus .INI files, and the Kevorkian algorithm. He then returns to MidiFitz, a MIDI program that reads an 88-key keyboard, parses the notes into chords in real time, and plays a matching bass line.


October 01, 1996
URL:http://www.drdobbs.com/cpp/c-programming/184409982

October 1996: C Programming

The Kindness of Strangers

Al Stevens

This column is mostly about how readers have helped me...

While building the supporting software for what has come to be known as the Al Stevens Cram Course on C/C++ series of CD-ROM, I published custom Setup and Uninstall programs in this column. Both programs share a common requirement. They need to find where the operating system stores entries for the Windows 95 Start Menu. Setup adds entries to that menu, and Uninstall removes them.

I observed that the location where those menus were stored changed sometime between the first beta of Windows 95 and the released version. I reported that the change had caused me some grief because I was trying to get the programs working by using the old location. Poking around in the file system, I found the new place, which, on my computer, is C:\Windows\

Start Menu\Programs. I assumed that you could call GetWindowsDirectory and append the \Start Menu\Programs path to the reported Windows subdirectory directory for adding application folders to make submenus and shortcuts to the executable files. It works on all the systems I tested it on except one. That system is a beta of Windows NT 4.0 with the new Windows 95 user interface. Without a lot of time to look further, I assumed that the 4.0 beta hadn't implemented the capability yet, and thought nothing more about it.

Several readers wrote to straighten me out. As it turns out, Windows 95 stores the location in a Registry entry; see Example 1.

Two API functions, SHGetSpecialFolderLocation and SHGetPathFromIDList, deliver the value of the string. Paul Sobolik, Dan Edwards, and Bruce Switzer sent essentially the same information about these functions. Dan sent the code that I need to implement it.

The Registry is a database that the operating system uses to store configuration parameters. Applications can add and retrieve parameters in the registry, too. I looked up those two functions in the API documentation and modified both programs. Example 2 is the code that I substituted for the call to GetWindowsDirectory and the appended constant path. These calls put the value of the Programs entry in the Registry into the wpath character array.

The Registry versus .INI Files

A reader named Alan Stevens, which is also my name and my son's name, wrote about several of the topics I discussed. He was concerned that my programs use .INI files instead of the Registry, believing that programs that use the Registry are somehow more "official." That could be true. I am, however, always reluctant to abandon something that I understand and that works very well. When I first saw Windows .INI files I was delighted. I prefer ASCII configuration files that you can whittle on with a generic text editor from a DOS diskette boot when nothing else works. Binary configuration files with closed architectures make me nervous. I don't like the mysterious files that underpin compressed drives. I don't like Novell's "bindery." I don't trust the proprietary file formats of the word processors that I use. I wouldn't use a compiler system that could not rebuild all its magic binary browser and class wizard files from scratch given only the ASCII source code. Once everyone realized that you shouldn't put everything for every application in WIN.INI, the .INI strategy worked very well. On the other hand, I have heard of Windows 95 installations that were disabled because the Registry got trashed. I don't even know the name of the Registry file much less how to fix it when the operating system won't boot.

If you are a devoted Win32 programmer, worry not. I'll come around in time.

The Kevorkian Algorithm

No single subject in this column has generated more immediate response than my request for the algorithm that permits an uninstall program to delete its own uninstall.exe file and remove the subdirectory that contains the uninstall.exe file. I called it the "Kevorkian Algorithm." I got many good suggestions and even some code from James Buchanan, Kevin Chipalowsky, Edward Diener, Mitchell Frey, Alan Hamilton, Tom Hornyak, Rob Lefebvre, Howard Lin, Matt Powel, and Bruce Switzer. A few other readers suggested that it could not be done.

Many readers cited an article by Jeffrey Richter in the January 1996 issue of the Microsoft Systems Journal, with which I should be familiar since MSJ and DDJ share the same publisher. I missed that article, though. Jeffrey's solution is simple and elegant. The last thing the uninstall program does is create a batch file in the root directory. The batch file deletes the uninstall program and loops back to that delete command as long as the uninstall program continues to exist. You have to do that because the uninstall program launches the batch file, and the first attempt to delete the uninstall program fails because the uninstall program is still running and its uninstall.exe file is still locked. After deleting the uninstall.exe file, the batch file removes the application's subdirectories and deletes itself, something that a batch file is permitted to do. Example 3 shows the code that the uninstall program uses to create and launch the batch file.

By the way, I read that Dr. Kevorkian recently applied for a permit to carry a concealed handgun. I can relate to that. I take a laptop when I travel on business.

MidiFitz Redux

One year ago this month I wrote about a program called "MidiFitz" that formed my Windows 95 programming habit and provided a tool for my other profession. MidiFitz is a MIDI program that reads an 88-key keyboard, presumably being played by a jazz pianist, parses the notes into chords in real time, and plays a matching bass line. In the year that followed, I used MidiFitz often, but only at home as a practice tool: not because it couldn't become roadworthy, but because I did not particularly like electronic keyboards as performance instruments. Until now.

Recently I took on a weekend gig at a piano bar. The instrument there was what I call a "beached" piano: unplayable after years on a cruise ship. The saloon owner was proud of it, though. As the old musician's joke goes, he'd just had it painted.

I had been wanting something to replace the aged Yamaha keyboard that drives MidiFitz, and so I used this gig as an excuse to go shopping. Electronic pianos have never held my respect. They don't sound like pianos and they don't feel like pianos. When I found the Roland RD-500, my attitude turned around. When played through a decent sound system, the 500 sounds so good and its hammer action is so like the real thing, that I decided to make the 500 my main instrument.

All of which caused me to take another look at MidiFitz. I'd like to take the program on the road, but it needs a Windows 95 computer with a decent sound card and a MIDI interface. They don't make high-quality sound cards for laptops. I wasn't sure what my options were.

My knowledge of MIDI was marginal, so I pulled out the venerable Jim Conger books, C Programming for MIDI and MIDI Sequencing in C, and the MIDI Programmer's Handbook by De Furia and Scacciaferro. All three are from M&T Books, very good, and, I am sorry to report, out of print. Then I went to some music stores where struggling rock musicians work during the day. Those guys know all about sequencers, synthesizers, samplers, sound modules, and such. After learning what MidiFitz does, they decided that I needed a MIDI interface for my laptop, along with a tone generator. The interface provides 88-key keyboard MIDI input to the laptop, and MIDI output signals from the laptop to the tone generator. The tone generator converts the MIDI signals into audio on a line out signal, which you connect to an amplifier.

After shopping with these guys and surfing the Internet, I bought a Yamaha MU5 tone generator at a music store and phone ordered from the MIDI Mailbox (800-324-6254) a MIDIMAN PORTMAN PC/P interface, which plugs into the laptop's parallel port and comes with Windows 95 and Windows 3.1 drivers. I already had a laptop with Windows 95 installed. And, of course, I had two digital keyboards-the old guy at home and the new RD-500 installed at the club.

Here's how the system is hooked up. I play the piano. Besides generating piano tones from my key presses through its own preamplifier system, the piano sends Note On MIDI messages through the PC/P to the laptop. MidiFitz reads the keystrokes, parses them into chords, develops a bass line, and sends the bass line notes through the PC/P to the MU5, which generates bass tones and sends them to the amplifier. I explained the internals of the parse and the bass-line generation last year. Figure 1 shows the connections.

Fixing MidiFitz

As originally designed, MidiFitz was fine for the practice studio, but it required too much user interaction for the piano bar where the customers expect you to be looking up and smiling instead of staring down at your laptop and mickeying with your mouse. MidiFitz could take its tempo from an external clock signal generated by the old keyboard's drum machine, but the RD-500 has no drum machine. I needed a simple way to tell the program what the tempo is. The obvious choice is to allow me to tap out the tempo on the space bar and have MidiFitz compute the timer value from the intervals between my taps, then start playing after four taps (or six, if we're in 3/4 time). And a-one, and a-two...

Furthermore, the selection of a tempo style (4/4, 3/4, Latin, and so on) required a mouse click, which is difficult when your attention is on the eighty-eight or the tip jar. I opted to change MidiFitz's interface to use simple key presses on the laptop's keyboard to set tempo styles.

I decided to remove a feature. I hate removing features. It's an admission of prior questionable judgment. MidiFitz remembered the chord patterns of a 32-bar song from the first chorus and played from that progression for subsequent 32-bar choruses until the song was over. Many songs are not 32 bars, I wasn't using the feature very often, and the program was cluttered enough, so out it came.

Then there was the procedural nature of the code. MidiFitz is a dialog-based MFC application that attends to the piano notes and plays bass notes. Until now everything was done in one program module. I wanted to add a drum machine, rhythm guitar, and perhaps some background strings. The instruments had to be encapsulated in a class hierarchy or the program would become unwieldy. Besides, I wanted to distance the operating modules of the program from the user interface in case I ever decide to port the application to another platform. As usual, I get around to writing a program correctly the second time around. The new version of MidiFitz is available for download.

To Win 3.1 or Not To Win 3.1

My last decision involved the operating system. I had used MidiFitz to learn about Windows 95 programming with Visual C++. It served that purpose well, but now it was going out into bars where the smoke is thick, beer gets slopped around, and an occasional merry-maker stumbles into the equipment. I wasn't sure that the Windows 95 laptop should be subjected to the seamier side of my other life. I happened to have an old Tandy 486/20 laptop with 4 MB of RAM and an 80-MB hard drive languishing on the shelf. (Remember when that was heavy iron?) It runs Windows 3.1, has an industrial strength look-and-feel, and isn't worth much.

MidiFitz doesn't use any of the advanced MFC or Win32 features. The Windows 3.1 SDK supports the multimedia extensions, which include the MIDI functions, so it should be possible to port this Win32 application to Windows 3.1. (I wonder if anyone else has done such a thing.)

The port to a clean compile went smoothly. Visual C++ 1.5 is included with the 32-bit-only versions 2.0 and later. My first reaction to returning to the older Visual Workbench was a pleasant surprise. The program is snappy without all the new flashy stuff-just enough tool buttons to get the job done, doesn't take forever to load, and the compiler seems faster than its feature-laden younger sibling. There is still a dance or two left in the old girl, as they say.

After eliminating a few try/catch/throw mechanisms, which are not implemented in VC++ 1.5, I had only to remove one function call from the program and one tag from the resource file to get a clean compile. They both involve 3-D controls, which VC++ 1.5 does not support.

The executable aborted right away. It seems that a dialog object in Windows 3.1 has not yet created its window when MFC calls the InitDialog function. I was using the m_hWnd variable as the parent of a MessageBox, and it would blow up because the handle was invalid. I built an overloaded OnCreate function, moved those operations there, and the program loaded and stood by waiting for me to beat off a tempo and start hammering out some flatted fifths.

I tapped the tempo on the spacebar, and the program immediately crashed, bringing the operating system down with it. A session with the debugger revealed that the crash happens as soon as the program calls the timeSetEvent function. The Multimedia Programmer's Reference says that the user-defined function specified as the "timeSetEvent's CALLBACK function" needs to be running in what they call a fixed DLL. The Win32 online API docs say nothing of the sort. This rang a bell in the recesses of my foggy memory. Rooting around in my e-mail files, I came up with a message from Jeff Koftinoff of Grand Forks, British Columbia, wherein, after reading my column last year, he told me about that requirement.

Constant Chord

It was getting close to the time to go to the gig, and I still didn't have a working, industrial-strength MidiFitz, so I decided to go with the Windows 95 version and fight to keep the beer sloshers away from my musical work station. I went in early and set everything up. Now I know why Dad advised me never to become a baseball catcher or a drummer. I used to meander in just before showtime, sit down, raise the lid of the piano, and go to work. When the gig was over, I closed the lid and went home. Now I have equipment to schlep, cables to unsnarl, and stuff to set up and test. No wonder rock musicians are so gaunt.

A quick test, and everything worked. The RD-500 sounded great through the house sound system, and MidiFitz stood by ready to play. I lit into the first song. After a measure or two, the few members of the audience who were still alert started looking at me suspiciously. What was that noise? I realized that the bass line that MidiFitz was playing had very little to do with the song that I was playing. The chord display showed that it was stuck on one chord. No matter what I played, the same chord dominated the bottom end. It sounded like one of those mop, rope, and washtub contraptions. I shut it down and restarted. Same thing. Sadly, I turned the system off and returned to my old style of solo piano, you know, like Dooley Wilson, the way they used to play before electronics. I was eager to get home and run some tests with the debugger.

Back home, everything worked perfectly. I made a special run to the joint the next day for another test with the 500, and MidiFitz was stuck again. The only thing different between the two sites was the keyboard, a Yamaha Clavinova at home and my brand new Roland RD-500 at work. I have a little program that Charles Petzold published that reads MIDI messages and displays them in a window. I ran that program against the Roland, and the messages were coming in loud and clear. Press a key and in comes a Note On message. Release the key and in comes a Note Off message. That looks right. I went home and ran the same test. I pressed a key and in came a Note On message just like before. I released the key and in came another Note On message with a velocity value of zero. Another bell sounded in my memory. Returning to the message from Jeff Koftinoff, I read these words.

Most keyboards send "Note On with Velocity!=0" when a key is pressed, and "Note On with Velocity==0" when a key is released. However some keyboards measure release velocity, and therefore send "Note Off with Velocity" when a key is released.

MidiFitz used the more common convention, marking a note as being released when its Note On message reported a velocity of zero. MidiFitz never sensed that a note had been released on the Roland because the Roland never sent that kind of message. MidiFitz was collecting Note On messages and keeping them in the note array from which it parsed each chord. That explained why the chord stopped changing after only a few chords were parsed.

Windows 95: Also Ran

Next, I returned to the problem of running MidiFitz on the Tandy laptop. I decided to postpone the Windows 3.1 port until I have time to deal with the implications of integrating a real-time DLL with the application. As an experiment, I installed Windows 95 on the old workhorse, not knowing what to expect. It runs Windows 3.1 just fine, but it stands today as living proof that Microsoft lied when they said that Windows 95 would run just as well as Windows 3.1. Or has everyone forgotten that promise made over a year ago? It didn't come true. Windows 95 on that old laptop is pure agony. Windows 3.1 is crisp and responsive.

That's okay, I don't need a fast operating system to run MidiFitz. Once it's loaded, the program runs alone, without needing much from the operating system. There are no disk files, no OLE, no DDE, and no threads. All MidiFitz needs is the MIDI-keyboard input and a timer callback to drive the rhythm section. I cranked it up and got a surprise. The timing was erratic. The bass and drum tempos were horrible. Notes were dropped. It reminded me of the Kiwanis Dixieland band I played in when I was a teenager. Then I recalled these words, also from Jeff Koftinoff:

MIDI is a real time thing. Windows 3.1, Windows 95, and Windows NT are not. Having some MIDI messages delayed by even 15 milliseconds is a noticeable problem...the reception time of a MIDI event is dependent on the uncertain time it takes Windows to schedule your window process.

Clearly, the latent overhead imposed by Windows 95 is too severe to allow the program to process within its time-critical envelope on a 20-Mhz computer. I'll have to complete that port to Windows 3.1 if the old Tandy is to be the brains behind MidiFitz. Later, perhaps.

The C++ Pop Quiz

In August I posed some interview questions that you might use to determine the knowledge of someone who is applying for a job as a senior C++ programmer. In September, I discussed the answers and the interview procedure. The September issue is not out as I write this column, so the responses I have received so far are based only on the questions and the underlying method that they imply.

Some readers took the test and sent me the answers. They all did well. I would expect regular readers of this column to score well on such a test. I'm not hiring programmers, but it was interesting to see the responses.

Two readers, Garry Simmons and Larry Smith sent thoughtful reactions to the whole idea. Garry said:

Give me a detailed, technical interview with a project lead and let them determine my relative guru-ness any day. In some firms, I may be a wizard. In other firms, I may only qualify to empty the bit bucket. It's all a matter of perspective.

I think firms need to take the hiring process more seriously and be willing to invest resources to it. It'd be nice to know that the next person I'm passed off to has had more than two minutes to review my resume. I realize the importance of buzzwords to get past HR, but once the real tire-kicking starts, it's important that both parties are willing to spend the time it takes to get to know each other's skills and needs. Project schedules are too demanding to risk bringing the wrong people on board.

Larry questions the validity of what he calls the "pop quiz" method of interviewing, and his arguments have merit. His concern is that the quiz concentrates on language details and ignores the larger issues of an applicant's ability to get the job done.

Rather than distinguishing between people who understand the language and those who do not, as Mr. Stevens clearly implies is the intent, all this accomplishes is to detect whether someone is accustomed to using the same [subset] of the language in the same manner as Mr. Stevens. Useful for putting together a team of programmers that all program just like Mr. Stevens, but not a good way to build up a diverse and powerful team that can address one's own weak points.

(If I weren't so modest, I would suggest that a team of programmers who all program like me is a powerhouse team, indeed, but then, I'm too modest.) Larry goes on to substitute what he thinks are more appropriate questions.

How much have you worked with [C++]

before?

Were those projects successful?

Why or why not?

What features of the language helped or hindered?

What would you do differently?

If you can't get a sense of how well a person knows the language from the responses to those questions then you are not knowledgeable enough to be trusted with hiring programmers.

Those are good questions, but the sentiment that follows them underlies the reason for what he calls my pop quiz. Most of the people doing the interviewing and, more importantly, making the hiring decisions would not understand the answers to Larry's questions or mine. Usually they recruit a technical person to sit in, someone who does not like to interview other people and does not know what to ask. My questions are meant to provide that person with a structured set of questions that not only reveal the applicant's knowledge of the language, but get a meaningful conversation going between the technical interviewer and the applicant. Based on subsequent conversations with Larry and others who have expressed interest, I am refining my questions.

I have a longstanding interest in how people are recruited and hired. My least-fulfilling interview came in my early 20s when I applied for a mainframe programmer's job with IBM. They gave me a programmer's aptitude test, which was their policy then and might still be. When the test was over, the interviewer told me that I scored 100 percent on the test, something that he had never seen. He had not thought it possible. (I was then and am now very proud of that score, even though I remember nothing about the test.) The interviewer then told me that IBM could not offer me a programming position because I lacked a college degree. I concluded then that if I had to work for idiots, I might as well be self-employed.

Example 1: Registry entry.

[HKEY_CURRENT_USER\Software\Microsoft\Windows\
CurrentVersion\Explorer\Shell Folder]
"Programs"="C:\WINDOWS\Start Menu\Programs"

Example 2: Finding the Start Menu Programs subdirectory.

#include <WINNETWK.H>
#include <SHLOBJ.H>
// ...
char wpath[MAX_PATH];
LPITEMIDLIST item;
SHGetSpecialFolderLocation(0, CSIDL_PROGRAMS, &item);
SHGetPathFromIDList(item, wpath);



Example 3: The Kevorkian algorithm.

// --- the uninstall.exe program and its subdirectories
char del[MAX_PATH];
strcpy(del, path);  // contains the app's installation path
strcat(del, "\\bin\\uninstall.exe");

ofstream bfile;
bfile.open("c:\\undo.bat");
if (!bfile.fail())  {
    bfile << ":top\n";
    bfile << "del " << del << endl;
    bfile << "if exist " << del << " goto top" << endl;
    bfile << "rmdir " << path << "\\bin" << endl;
    bfile << "rmdir " << path << endl;
    bfile << "del c:\\undo.bat" << endl;
    bfile.close();
    char* cp = getenv("COMSPEC");
    if (cp) {
        char cmd[MAX_PATH + 20];
        sprintf(cmd, "%s /C c:\\undo.bat", cp);
        WinExec(cmd, SW_MINIMIZE);
    }
}

Figure 1: MidiFitz connections.

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