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

Design

OS/2 2.x Initialization Files and Profile Management


MAY93: OS/2 2.X INITIALIZATION FILES AND PROFILE MANAGEMENT

Here's an initialization-file browser and editor based on the Profile Manager API

This article contains the following executables: INITOR.ARC

Derrel Blain, Kurt Delimon, and Jeff English

Derrel, Kurt, and Jeff are members of the Systems Development team at Micrografx. They are the authors of Real-world Programming for OS/2 (Howard Sams, 1993), on which this article is based. You can contact them at 1303 Arapaho, Richardson, TX 75081 or on CompuServe at 70743,351.


According to IBM, OS/2 2.x initialization files are "a convenient place to store information between sessions." When you get to know them, however, they're much more useful. This article has two purposes. One is to give you a basic understanding of initialization files for OS/2 and the API calls used to interact with them. The other is to provide you with a useful initialization-file editor.

An OS/2 2.x initialization file is just like any other file. It's not hidden or read only, nor does it need to be in any special place on the disk. Initialization files can be copied, renamed, deleted, and so forth. Information can easily be added to or deleted from the file. However, OS/2 initialization files (INI files) are binary, unlike those of Microsoft Windows, which are ASCII. This makes OS/2 INI files less approachable since a specific API is needed to interact with them.

The Profile Manager API

OS/2 2.x provides a number of function calls to open, write, read, and close initialization files. For example, an application may wish to save its current screen location and size. It can do so in an initialization file using functions from the Profile Manager API. When the application is restarted, it should look for its INI file entry, again using functions from the Profile Manager API. It can then use the information it obtained from the INI file to resize itself accordingly.

INI files are built using the Profile Manager API within OS/2. Two special INI files are used by the operating system; any other file created using the Profile Manager API can be used for various purposes, including those more involved than simply storing information between instances of an application. The Profile Manager API entries simply provide a consistent manner to interact with these files. Table 1 lists this API's functions. This Profile Manager API should also be used to add information to an INI file as well as delete from it, rather than creating your own functions.

Table 1: Profile Management API.

  Function               Description
  -----------------------------------------------------------------------

  PrfCloseProfile        Closes a private profile file.
  PrfOpenProfile         Opens a private profile file.
  PrfQueryProfile        Retrieves full path name for system
                         initialization files (OS2.INI and OS2SYS.INI).
  PrfQueryProfileData    Retrieves information from the profile file.
  PrfQueryProfileInt     Retrieves an integer from the profile file.
  PrfQueryProfileSize    Retrieves size to hold desired information from
                         the INI file.
  PrfQueryProfileString  Retrieves a string from the profile file.
  PrfReset               Resets Presentation Manager intialization files
                         with new files.
  PrfWriteProfileData    Writes or deletes binary data in the profile
                         file.
  PrfWriteProfileString  Writes or deletes a string in the profile file.

Using the Profile Manager API results in a consistent format in which you and your applications interact with these files. This interaction can involve application-specific data, information contained within the operating system's INI files, and even data kept in the INI files of other applications.

An application can create its own INI file in which to store data, or it can use the system initialization files. The latter is often preferable. OS/2 uses two INI files to store system data and configuration information. OS2.INI and OS2-SYS.INI are the default names for the user and system profile files, respectively. These files can be found in the \OS2 directory. They have a considerable number of entries that include information about system display colors, printers, printer queues, settings for the serial ports, and much more.

These system initialization files, like all initialization files built using the Profile Manager API, have a three-tiered structure. The highest level of organization is the application name. An INI file may have one or more application names, each of which may in turn have one or more key names. Both the application name and the key name are NULL-terminated strings that may not exceed 1024 bytes (including the terminating NULL character). Each key name has a data stream associated with it. This is the key data, and it may be a NULL-terminated string or a block of binary data that does not exceed 64 Kbytes. For example, the default OS2.INI file contains the application name (PM_colors, for example), a key name (like DialogBackground), and key data (such as 204 204 204, which represents an RGB shade of grey). Note that the key data is also a NULL-terminated string and that case is preserved in all stored strings.

The INITOR Application

Much of this information is hidden from you because of the binary form of OS/2 initialization files--and IBM doesn't provide you with a file browser. Consequently, we wrote our own initi lization-file browser and editor called INITOR, which is based on the Profile Manager API and is designed to display the application name, key name, and key data simultaneously; see Table 2. To this end, it uses two list boxes and an application-defined data window. A row of buttons across the bottom of INITOR give access to pop-up menus that display system or private INI files and enable you to add or delete entries in the open file.

Table 2: Files that make up the INITOR application.

  File        Description
  -----------------------------------

  INITOR.C    INITOR application's C
              code.

  INITOR.DEF  Module-definition file.

  INITOR.H    An include file.

  INITOR.ICO  The application's icon
              file.

  INITOR.RC   INITOR'S resource file.

  INITOR.DLG  INITOR'S dialog-definition
              file.

Because of space constraints, we'll discuss only that portion of the code that deals directly with enumerating the values in INI files using the Profile Manager API; see Listing One (page 84). All the required files (resource file, icon file, and others) are available electronically; see "Availability," page 7.

Before going further, remember that if you intend to edit any system file, you should be sure to save a copy of that file in a safe place. One way of doing this is to let OS/2 automatically back up your current INI files each time you start your system. You will always have a copy of your INI files as they existed at system startup if you add the following line to your CONFIG.SYS file: CALL=C:\OS2\XCOPY.EXE C:\OS2\ OS2*.INI C:\OS2\*.BAK.

Initialization-file Handles

Reading from or writing to an INI file requires an initialization-file handle HINI. An INI file must be opened to get an INI-file handle. The user and system profile files are opened by the system at startup as a result of a call to PrfReset. Private INI files must be opened using the PrfOpenProfile function. To access the system INI files, three predefined handles are supplied by the operating system: HINI_USERPROFILE, for reading/writing user profile; HINI_SYSTEMPROFILE, for reading/writing system profile; and HINI_PROFILE, for reading both user and system profile and writing user profiles. (The term "profile file" is used almost interchangeably with "initialization file," apparently following a historical convention passed down from Windows coding.)

Enumerating Entries in an INI File

Though differing API entries are called, each of the three levels in the structure of initialization files is enumerated in much the same manner. The INITOR sample application begins this process by enumerating the application names. The primary function for this in INITOR is enum_app_names. We describe the process of that function here, but first, let's take a look at the code for enum_key_name and get_key_data. Notice that their structure and process is similar to that of enum_app_names. This is understandable since the logical structure of INI files is the same at each of the three levels. The enum_app_names function in INITOR takes two parameters: an initialization-file handle and the ID of a string to load and set in the title bar. This ID is defined in the resource file. enum_app_names will enumerate all application names in the INI file by calling PrfQueryProfileString. The application names are then added to the application list box displayed by the INITOR application. The PrfQueryProfileString is defined as in Table 3.

Table 3: Defining PrfQueryProfileString. PrfQueryProfileString (hIni,pAppName,pKeyName,pDefault,pBuffer, ulBufferSize)

  -------------------------------------------------------------------------

  HINI   hIni        Initialization-file handle.

  PSZ    pAppName    Pointer to a NULL-terminated string that identifies
                     the application name to be queried.

  PSZ    pKeyName    Pointer to a NULL-terminated string that identifies
                     the key name to be queried.

  PSZ    pDefault    Pointer to a NULL-terminated string to be copied to
                     pBuffer if the requested entry is not found.

  PSZ    pBuffer     Pointer to the location where return string is to be
                     copied.

  ULONG  ulBuffSize  Length in bytes of the memory pointed to by pBuffer.

PrfQueryProfileString performs a case-insensitive search for the string pointed to by pKeyName in the application section identified by pAppName. If a match is found, the key-name data is copied to pBuffer, and the number of bytes--including the NULL terminator copied--is returned. If a match is not found, the contents of pDefault are copied to pBuffer. If pBuffer is NULL, nothing is copied to pBuffer and 0 is returned. By specifying NULL for the pAppName parameter, PrfQueryProfileString returns all application names in the INI file as an array of NULL-terminated strings. The last string will be double-NULL terminated.

If NULL is passed as the pKeyName parameter and pAppName is the pointer to a string which is found in the file, all key names in that application section are returned as an array of NULL-terminated strings. As with the enumerated application names, the last string will be double-NULL terminated.

Since the number and size of the entries in an INI file varies, enum_app_names determines the space required to hold the application names by calling PrfQueryProfileSize, which takes a set of parameters similar to those of PrfQueryProfileSize; see Table 4. enum_app_names will pass NULL for both the pAppName and pKeyName since we want to know how much memory is required to hold all of the application names. If the function in Example 1(a) is successful and the length returned in ulSize in non-NULL, memory is allocated and the strings are retrieved.

Table 4: enum_app_names determines the space required to hold the application names by calling PrfQueryProfileSize.

  PrfQueryProfileSize (hIni,pAppName,pKeyName,pDataLength)
  ------------------------------------------------------------------------

  HINI    hIni         Initialization-file handle.

  PSZ     pAppName     Pointer to a NULL-terminated string that
                       identifies the application name to be queried.

  PSZ     pKeyName     Pointer to a NULL-terminated string that
                       identifies the key name to be queried.

  PULONG  pDataLength  Pointer to a ULONG which will contain the length
                       of keyname data if the function is successful.

Example 1: (a) Returning the number of bytes (including NULLS) copied to pData; (b) allocating memory and retrieving strings.

  (a)

  if(PrfQueryProfileSize(hini,NULL,NULL,(PULONG)&ulSize) && ulSize)

  (b)

  DosSubAlloc(pMem.(PPVOID)&pData,ulSize);
  if(PrfQueryProfileString(hini,NULL,NULL,"No Entries",pData,ulSize))

The strings in Example 1(b) are copied to the memory pointed to by pData, and the actual number of bytes (including NULL) copied to pData is returned. Since we already know the length of the returned data, the return length is ignored. The application list box is disabled and its contents are deleted. Disabling the list box prevents redrawing after each string is added. The array of application names is walked, and each string is added to the list box in ascending order. When all items are added, the first item is selected and updates to the list box are re-enabled. The memory used for the application names is freed. By selecting an entry in the list box, a WM_CONTROL message with a notification code of LN_SELECT is sent to the owner window. The owner window calls enum_key_name, which will fill up the key-name list box in a similar fashion.

After the key-name list box is filled by enum_key_name, the first entry is selected causing another WM_CONTROL message to be sent to the owner window. This time the owner calls get_key_data. If a data item currently exists, the memory for it is freed and PrfQueryProfileSize is called to determine the storage requirements of the new data. The data is retrieved by calling PrfQueryProfileData since the data may be a NULL-terminated string or a stream of binary data. The PrfQueryProfileData is defined as shown in Table 5. As with PrfQueryProfile-String, the search is case insensitive and passing a NULL for either pAppName or PKeyName has the same effect described earlier. If the data is successfully returned, the data window is invalidated, causing the new data to be displayed.

Table 5: PrfQueryProfileData function definition.

  PrfQueryProfileData(hIni,pAppName,pKeyName,pKeyData,(pDataLength)
  ------------------------------------------------------------------------

  HINI    hIni         Initialization-file handle.

  PSZ     pAppName     Pointer to a NULL-terminated string that
                       identifies the application name to be queried.

  PSZ     pKeyName     Pointer to a NULL-terminated string that
                       identifies the key name to be queried.

  PSZ     pBuffer      Pointer to the location where the data is to be
                       copied.  The return data is not NULL terminated
                       unless the last byte of the data stored was a
                       NULL.

  PULONG  pDataLength  Pointer to a ULONG that contains the length of
                       buffer pointed to by pBuffer.  If the function
                       is successful the value will be replaced with the
                       number of bytes actually copied to pBuffer.

Using Your Own INI File

For the purpose of testing the application, we've written INITOR so that it saves its screen location in the system INI file. Applications created for commercial distribution should store their configuration information in a private initialization file. This prevents the loss of settings if OS/2 is reinstalled or the system INI files are rebuilt by the user. Holding down Alt+F1 for 20 seconds before the first OS/2 logo panel appears will reset the default desktop configuration. Before data may be written to a private INI file, it must be opened using PrfOpenProfile. If the file exists, it is opened; otherwise, a new file is created. An initialization-file handle is returned if the function is successful; NULL is returned if an error occurs. The PrfOpenProfile function requires an anchor-block handle and a pointer to a NULL-terminated string that contains the filename.

When you select the Open Private menu item from the display pop-up menu, INITOR invokes the common open-file dialog. You may then open or create any private INI file. While most initialization files use the .INI file extension, it is not required. The PrfOpenProfile function will fail if the current system or user initialization filenames are specified. Attempting to open an initialization file created by Windows applications in a WIN/OS2 session will also fail since these files are stored in a different format.

Once a private profile file is opened or created it may be queried or written to using the same API as the system initialization files. However, when your application is through updating the file, it should be closed using PrfCloseProfile. This function takes the INI file handle returned from a PrfOpenProfile call as its only parameter and cannot be used to close one of the system initialization files. If successful, the function returns True or False.



_OS/2 2.X INITIALIZATION FILES AND PROFILE MANAGEMENT_
by Derrel R. Blain, Kurt Delimon, Jeff English


[LISTING ONE]
<a name="0126_000f">

void APIENTRY enum_app_names(HINI hini,USHORT usStringID)
/*-----------------------------------------------------------------*\
This function will query the size required to hold all of the application names
for the current INI file. If the file contains entries, a temporary block of
memory is allocated and the application names are queried from PM. The strings
are then added to the application name listbox and the memory is freed. The
first entry in the listbox is selected, causing its owner, the client, to be
notified. The client window will then fill the key name listbox by calling
enum_key_name function.
\*-----------------------------------------------------------------*/
{
     PVOID pData;
     PBYTE pCurrent;
     ULONG ulSize = 0L;
     if (PrfQueryProfileSize(hini,NULL,NULL,(PULONG)&ulSize) && ulSize)
     {
         DosSubAlloc(pMem,(PPVOID)&pData,ulSize);
         if(PrfQueryProfileString(hini,NULL,NULL,"No Entries",pData,ulSize))
         {
             pCurrent = pData;
             WinEnableWindowUpdate(hAppLBox,FALSE);
             WinSendMsg (hAppLBox,LM_DELETEALL,NULL,NULL);
             while (*pCurrent)
             {
                 WinSendMsg (hAppLBox,LM_INSERTITEM,(MPARAM)LIT_SORTASCENDING,
                                                             (MPARAM)pCurrent);
                 while(*pCurrent)
                     pCurrent++;
                 pCurrent++;
             }
             WinSendMsg (hAppLBox,LM_SELECTITEM,MPFROMSHORT(0),

                 MPFROMSHORT(TRUE));
             WinEnableWindowUpdate(hAppLBox,TRUE);
             if (usStringID)
             {
                 WinLoadString (hab,0,usStringID,MAX_TITLE,szTitle);
                 WinSetWindowText(hWndFrame,(PSZ)szTitle);
             }
         }
         DosSubFree(pMem,pData,ulSize);
     }
     else
     {
         WinAlarm(HWND_DESKTOP,WA_WARNING);
         WinEnableWindowUpdate(hAppLBox,FALSE);
         WinSendMsg (hAppLBox,LM_DELETEALL,NULL,NULL);
         WinSendMsg (hAppLBox,LM_INSERTITEM,(MPARAM)LIT_SORTASCENDING,"No
                                                                     Entries");
         WinSendMsg (hAppLBox,LM_SELECTITEM,MPFROMSHORT(0),MPFROMSHORT(TRUE));
         WinEnableWindowUpdate(hAppLBox,TRUE);
         // No entries exist force the data window to free up it's
         // data buffer and repaint.
         get_key_data("","");
         WinInvalidateRect(hDataWnd,NULL,FALSE);
     }
}
void APIENTRY enum_key_name(PSZ pAppName)
/*-----------------------------------------------------------------*\
This function will query the size required to hold all of the key names for the
current application name in the current INI file. If the application name
contains key strings, a temporary block of memory is allocated and the key
names are queried from PM. The strings are then added to the application name
listbox and the memory is freed. The first entry in the listbox is selected
causing its owner, the client, to be notifed. The client window will then fill
the data window by calling get_key_data.
\*-----------------------------------------------------------------*/
{
     PVOID pData;
     PBYTE pCurrent;
     ULONG ulSize = 0L;
     if (PrfQueryProfileSize(hCurrentIni,pAppName,NULL,(PULONG)&ulSize) &&
                                                                        ulSize)
     {
         DosSubAlloc(pMem,(PPVOID)&pData,ulSize);
         if(PrfQueryProfileString(hCurrentIni,pAppName,NULL,"No Entries",
                                                                 pData,ulSize))
         {
             pCurrent = pData;
             WinEnableWindowUpdate(hKeyLBox,FALSE);
             WinSendMsg (hKeyLBox,LM_DELETEALL,NULL,NULL);
             while (*pCurrent)
             {
                 WinSendMsg (hKeyLBox,LM_INSERTITEM,(MPARAM)LIT_SORTASCENDING,
                                                             (MPARAM)pCurrent);
                 while(*pCurrent)

                     pCurrent++;
                 pCurrent++;
             }
             WinSendMsg (hKeyLBox,LM_SELECTITEM,MPFROMSHORT(0),
                 MPFROMSHORT(TRUE));
             WinEnableWindowUpdate(hKeyLBox,TRUE);
         }
         DosSubFree(pMem,pData,ulSize);
     }
     else
     {
         WinEnableWindowUpdate(hKeyLBox,FALSE);
         WinSendMsg (hKeyLBox,LM_DELETEALL,NULL,NULL);
         WinEnableWindowUpdate(hKeyLBox,TRUE);
         // No entries exist force the data window to free up it's
         // data buffer and repaint.
         get_key_data("","");
         WinInvalidateRect(hDataWnd,NULL,FALSE);
     }
}
void APIENTRY get_key_data(PSZ pAppName,PSZ pKeyName)
/*-----------------------------------------------------------------*\
This function will attempt to query the keydata for the current application-key
pair. If key data currently exists, it is freed before the new entry is
queried. If successful, a message is sent to the client to update the key data
window title with the size of the new data. The data window is invalidated.
\*-----------------------------------------------------------------*/
{
     if (pKeyData)
     {
         DosSubFree(pMem,pKeyData,ulKeySize);
         pKeyData  = NULL;
         ulKeySize = 0;
     }
     if (PrfQueryProfileSize(hCurrentIni,pAppName,pKeyName,(PULONG)&ulKeySize)
                                                                  && ulKeySize)
     {
         DosSubAlloc(pMem,(PPVOID)&pKeyData,ulKeySize);
         if(PrfQueryProfileData(hCurrentIni,pAppName,pKeyName,pKeyData,
                                                           (PULONG)&ulKeySize))
         {
             // Update the title
             WinSendMsg(hDataWnd,WM_UPDATE_TITLE,0L,0L);
             // Force the data window to repaint
             WinInvalidateRect(hDataWnd,NULL,FALSE);
         }
     }
}












Copyright © 1993, Dr. Dobb's Journal


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.