From writing to reading. (GetPrivateProfileString routine) (Programming Power) (Column)
by Tom Campbell
In last month's column, we looked at the Windows routine WritePrivateProfileString, which lets you create init files. Init files are read in when your application starts up and contain system variables in this format: [SectionName] Entry-Name=AddString.
The section name, which appears in brackets, may have more than one entry line. An entry consists of an entry name (called EntryName in our example above) and a value (called AddString above) for that entry. A typical entry for [Printer] might contain Port=LPT1 if your printer is connected to the first parallel port, and if you use a Hewlett-Packard LaserJet, it would be PrinterName=LaserJet IIIP.
In this month's column, we'll look at the routine GetPrivateProfileString, which reads the value for an entry. For example, if you were to pass it the filename myprog.ini, the section [Printer], and the entry Port, it would pass back LPT1. If there were no init file available, it would return the default value you provided when you called the routine.
The code we'll write this month is straightforward and builds on last the code we wrote last month. As each line of code from the init file is read in, the first character is checked to see if it's a left square bracket. If it is, the entire line is assumed to be a section, such as [Printer]. It's then forced to uppercase.
When the routine first started, the requested section name was also forced to uppercase and surrounded with square brackets. If the two sections match, the SectionFound flag will be set. As long as it's set, incoming lines will be checked for left square brackets. If they are present, the search has failed. If they are absent, the lines are assumed to be entries in the format EntryName=AddString, and the familiar Parse$ is used to halve the line.
If Left Side is the requested entry, a match has succeeded, and whatever is on the right side of the line is returned by the function. If the entry is never found, a default value is returned.
Although this routine is modeled after a Windows API call by the same name, it differs in several ways. First, what is called the section here is called the application name by Windows. Calling it an application is a misnomer; it's a holdout from the old days when all Windows apps used their own sections of the win. ini file to store configuration values. I'm not sure why the Windows documentation doesn't reflect this change in direction, but I decided not to perpetuate the mistake.
Second, the Windows call has an additional parameter not included with my version: the length of the buffer used to hold the string returned by this function. It may seem like a horrible kludge to BASIC programmers--and it is.
Windows is written in C, and C has no concept of strings; they're nothing more than arrays of characters ending with a zero byte. So if you want to copy a string in C, you have to use a runtime library routine to do it (not the handy-dandy = operator of BASIC), and you have to make sure you've dimensioned a character array big enough to hold the copied string. If you haven't dimensioned that array, C will copy the string anyway, assuring at the least that your program will work improperly and typically causing the system to hang.
Finally, the Windows API call requires that you pass it a buffer for the value returned from the init file entry. Its size must be less than or equal to the amount passed in the size argument. Again, this parameter is omitted, and the value is returned by the function. And again, a C programmer wouldn't have this flexibility because C doesn't have a string data type, so it doesn't support functions returning strings.
A side note: Windows NT, the higher-end version of Windows for workstations and non-Intel processors, doesn't use init files. (Technically, it can, but doing so is considered bad form, and they are supported only for compatibility.) Because NT was designed with reliability as its most important feature, text files for the purposes of system configuration were ruled out, and a registration database replaces them. The main reason is that it's easy for a text file to be damaged, yet not appear to be so. Suppose, for example, the last line of a configuration file contained the text Printer=LaserJet IIIP but the file was accidentally truncated to Printer=LaserJe. Windows would look for a printer driver, would not find one matching LaserJe, and would return an error, even if the IIIP were connected and ready to run. Using a more descriptive binary file format, NT is able to detect damaged files and proceed accordingly, by attempting an automatic recover or by notifying the system administrator.
Another reason to avoid text files is that binary files offer more flexibility. In an init file, the line Copies= 10 is all text, so you have to use Val to convert the string 10 to computer-readable format. More complex data structures are out of the question. A binary file lets any kind of data be represented in its native format.