PMGuide - Atom Tables
Reprint Courtesy of International Business Machines Corporation, © International Business Machines Corporation
Atom tables enable applications to generate unique identifiers and manage strings. This chapter describes how to use atom tables in PM applications.
About Atom Tables
An atom table is an operating system mechanism that an application uses to obtain unique, system-wide identifiers to manage strings efficiently. An application places a string, called an atom name, into an atom table and receives a 32-bit integer value, called an atom, that the application can use to access that string.
System Atom Table
The system atom table is available to all applications. When an application places a string in the system atom table, any application that has the atom name can obtain the atom by querying the system atom table.
An application that defines messages, clipboard-data formats, or dynamic data exchange (DDE) data formats that are intended for use among applications must place the names of the messages or formats in the system atom table. It avoids possible conflicts with messages or formats defined by the system or other applications, and makes the atoms for the messages or formats available to other applications. Applications should use names that are not likely to be used by other applications for other purposes.
Some PM functions enable applications to use atoms in parameters that normally take pointers to strings. For example, WinRegisterClass takes a pointer to a string for its pszClassName parameter. WinRegisterClass places the class name string in the system atom table. Afterward, an application can query the system atom table to obtain the atom, then use the atom as the pszClientClass parameter of WinCreateStdWindow. This process can save space in the data segment of applications that create many windows of the same private class. Every atom table has a unique handle. An application must obtain the handle before performing any atom operations. To obtain the handle of the system atom table, an application uses WinQuerySystemAtomTable. The atom-table handle returned by this call is used for all other atom functions.
Private Atom Tables
An application can use a private atom table to efficiently manage a large number of strings that are used only within the application. The strings in a private atom table, and the resulting atoms, are available only to the application that created the table.
An application that must use the same string in a number of data structures can save data-segment space by using a private atom table. Rather than copying the string into each data structure, the application can place the string in the atom table and use the resultant atom in the data structures. In this way, a string that appears only once in the data segment still can be used many times in the application.
Applications also can use private atom tables to save time when searching for a particular string. To perform a search, an application must place the search string in the atom table only once, then compare the resultant atom with the atoms in the relevant data structures. This usually is faster than doing string comparisons.
Every atom table has a unique handle. An application must obtain the handle before performing any atom operations. To create a private atom table and obtain its handle, an application must use WinCreateAtomTable. The atom-table handle returned by this call must be used for all other atom functions.
An application that no longer needs its private atom table should call WinDestroyAtomTable to destroy the table and free the memory that the system allocated for the table.
Atom Types
Applications can use two types of atoms: string and integer.
String Atoms
Applications pass null-terminated strings to atom tables and receive string atoms (32-bit integers) in return. String atoms have the following properties:
- The maximum number of string atoms allowed is 16K. The values of string atoms are from 0xC000 through 0xFFFF.
- The maximum amount of data that an atom table can store is 60K. This includes the control data that the operating system uses to manage the atom table (32 bytes for the table plus 8 bytes for each string atom).
- The maximum length of an atom name is 255 characters. A zero-length string is not a valid atom name.
- Case is significant when searching for an atom name in an atom table, and the entire string must match. No substring matching is performed.
- A usage count is associated with each atom name. The count is incremented each time the atom name is added to the table and decremented each time the atom name is deleted from the table. This allows different users of the same string atom to avoid destroying each other's atom names. When the usage count for an atom name equals zero, the system removes the atom and atom name from the table.
Integer Atoms
Integer atoms differ from string atoms as follows:
- Integer atoms are values from 0x0001 through 0xBFFF. The values of integer atoms and string atoms do not overlap, so the two types of atoms can be intermixed.
- The string representation of an integer atom is ddddd, where ddddd are decimal digits. Leading zeros are ignored.
- There is no usage count nor storage overhead associated with an integer atom.
The operating system uses integer atoms to detect whether the same window class name is being defined more than once. The system defines the predefined window class names using integer atoms as constants. When an application registers a window class, the system enters the specified class name in the system atom table. The system then compares the resultant atom with the predefined window-class constants and with the atoms representing the application-defined class names registered earlier. To be able to do this comparison, the system must express the preregistered class names as atoms. By defining the class names as integer atoms, the system ensures that the atoms do not conflict with the string atoms it generates for application-defined class names.
Atom Creation and Usage Count
An application creates an atom by calling WinAddAtom, passing an atom-table handle and a pointer to a string. The system searches the specified atom table for the string. If the string already resides in the atom table, the system increments the usage count for the string and returns the corresponding atom to the application. Repeated calls to add the same atom string return the same atom. If the atom string does not exist in the table when WinAddAtom is called, the string is added to the table, its usage count is set to 1, and a new atom is returned.
An application can retrieve the usage count associated with a given atom using WinQueryAtomUsage. By obtaining the usage count, an application can detect whether other applications, or other threads within the application, are using the same atom.
Atom Deletion
An application calls WinDeleteAtom when it no longer needs to use an atom. WinDeleteAtom reduces the usage count of the corresponding atom by 1. When the usage count reaches zero, the system deletes the atom name from the table.
Atom Queries
An application can find out if a particular string is already in an atom table by using WinFindAtom. WinFindAtom searches the atom table for the specified string and, if the string is there, returns the corresponding atom.
There are two functions that an application can use to retrieve a string from an atom table, provided that the application has the atom corresponding to the desired string. The first, WinQueryAtomLength, returns the length of the string corresponding to the atom. This allows the application to create a buffer of the appropriate size for the string. The second, WinQueryAtomName, retrieves the string and copies it to the buffer.
Atom String Formats
The second parameter to WinAddAtom and WinFindAtom, pszAtomName, is a pointer to zero-terminated string. An application can specify this pointer in four ways, as shown in the following table:
Format Name | Description |
---|---|
"!",atom | Points to a string in which the atom is passed indirectly, as a value. |
#ddddd | Points to an integer atom specified as a decimal string. |
ulong: FFFF(low word) | Passes an atom directly. The atom is in the low word of the pszAtomName parameter. The operating system uses this format to add predefined window classes to the system atom table. |
string atom name | The pointer is to a string atom name. Applications typically use this format to add an atom string to an atom table and receive an atom in return. |
The "!",atom and ulong: FFFF(low word) formats are useful when incrementing the usage count of an existing atom for which the original atom string is not known. For example, the system clipboard manager uses the ulong: FFFF(low word) format to increment the usage count of each clipboard-format atom when that format is placed on the clipboard. By using this format, the atom is not destroyed even if the original user of the atom deletes it, because the usage count still shows that the clipboard is using the atom.
Using Atom Tables
This section explains how to create unique window-message atoms, dynamic data exchange (DDE) formats and a clipboard format.
Creating Unique Window-Message Atoms
You must create atoms for your application-defined window messages if other applications are likely to recognize those messages. For example, your application might communicate with another application by using an agreed-upon message that is not defined by the system. Both applications must use the same string identifier for the shared message type-for example, OUR_LINK_MESSAGE. Each time the applications run, they add this string to the system atom table and receive an atom in return. Both applications register the same string in the system atom table, so they both receive the same atom. Then, this atom can be used to identify the message without conflicting with other system-wide message identifiers. A consequence of using atoms to identify a window message is that the message cannot be decoded as a C-language case statement, as usually done, because the value of the atom cannot be known until run time. Instead, you must add a default case that checks the value of the message against the value of the atoms you have registered. The following sample code fragment shows how to add an application-defined message string to the system atom table, then use the resultant atom to broadcast and receive the message:
#define IDM_BROADCAST 25 HATOMTBL hatomtblSystem; /* System atom table handle */ ATOM atomLinkMessage; /* Atom message */ /* Message text */ UCHAR szLinkMessage[] = "OUR_LINK_MESSAGE"; MRESULT EXPENTRY ClientWndProc(HWND hwnd,ULONG msg, MPARAM mp1,MPARAM mp2) { /* At create time obtain atom for text message */ switch (msg) { case WM_CREATE: hatomtblSystem = WinQuerySystemAtomTable(); atomLinkMessage = WinAddAtom(hatomtblSystem, szLinkMessage); return FALSE; /* Broadcast text message */ case WM_COMMAND: if (SHORT1FROMMP(mp1) == IDM_BROADCAST) { WinBroadcastMsg(HWND_DESKTOP, atomLinkMessage, (MPARAM) NULL, (MPARAM) NULL, BMSG_DESCENDANTS | BMSG_POSTQUEUE); } return FALSE; default; /* Check for the atom representing "OUR_LINK_MESSAGE" */ if (msg == atomLinkMessage) return DoOurMessage(...); break; } /* Execute default window procedure */ return WinDefWindowProc(hwnd,msg,mp1,mp2); }
Creating DDE Formats and a Unique Clipboard Format
Applications that define their own clipboard or DDE formats must register those formats in the system atom table to avoid conflicting with the predefined formats and any formats used by other applications. The following sample code fragment shows how to register a custom format:
#define MAX_BUF_SIZE 128 HAB hab; /* Anchor block handle */ HATOMTBL hatomtblSystem; /* System atom table handle */ ATOM atomFormatID; /* Atom message */ PSZ pszSrc, pszDest; /* String pointers */ BOOL fSuccess; CHAR szClipString[MAX_BUF_SIZE]; APIRET rc; /**********************************************************************/ /* Get the handle of the system atom table, */ /* then add the format name to the table. */ /**********************************************************************/ /* System atom table handle */ hatomtblSystem = WinQuerySystemAtomTable(); /* Register format string */ atomFormatID = WinAddAtom(hatomtblSystem,"SuperCAD_FORMAT") /**********************************************************************/ /* Obtain data and write data to buffer (szClipString). */ /**********************************************************************/ /* Open the clipboard */ if (WinOpenClipbrd(hab)) { /* Allocate a shared memory object for the text data */ if (!(rc = DosAllocSharedMem( (PVOID)&pszDest, /* Pointer to shared memory */ /* object */ (PSZ) NULL, /* Use unnamed shared memory */ (ULONG)strlen( szClipString) + 1, /* Amount of memory */ PAG_WRITE | /* Allow write access */ PAG_COMMIT | /* Commit the shared memory */ OBJ_GIVEABLE))) /* Make pointer giveable */ { /* Setup the source pointer to point to text */ pszSrc = szClipString; /* Copy the string to the allocated memory */ while (*pszDest++ = *pszSrc++); /* Clear old data from the clipboard */ WinEmptyClipbrd(hab); /* Pass the pointer to the clipboard in custom format. */ /* Notice that the pointer must be a ULONG value. */ fSuccess = WinSetClipbrdData(hab, /* Anchor block handle */ (ULONG) pszDest, /* Pointer to text data */ atomFormatID, /* Custom format ID (atom) */ CFI_POINTER); /* Passing a pointer */ /* Close the clipboard */ WinCloseClipbrd(hab); } }