Guidelines for Allocating Instance (Per-Window) Data

Last reviewed: July 23, 1997
Article ID: Q76103
3.00 3.10 WINDOWS kbprg

The information in this article applies to:

  • Microsoft Windows Software Development Kit (SDK) for Windows versions 3.1 and 3.0

SUMMARY

If an application creates multiple windows of a single window class, that application must carefully manage data for those windows. Undesired results may occur when windows share the same global data because modifications of that data will affect all windows.

If an application has a window that normally relies on global data and the application creates multiple instances of that window, whatever was kept in global data must be kept in instanced data. The optimal method is to store a private data structure in global or local memory and to store a handle to that memory in the window's so-called "extra" bytes. Alternatively, the window may store the handle in its property list.

This article discusses the concerns and methods for storing extra data with windows using both window extra bytes and property lists.

MORE INFORMATION

  1. Application and Window Instances, Sharing Data

The word "instance" usually refers to a particular copy of an application that is currently running in the system. The term "instance" means "copy of data"; each instance of an application has a separate data segment containing global and static data, the application's stack, and the application's local heap. Different instances of the same application have only very limited access to each other's data (through the use of the GetInstanceData function).

Every window is an "instance" of a particular window class. A window is represented by a data structure allocated from USER's local heap (identified by hWnd) that contains information specific to that window, such as its window style, and a pointer to its window procedure. Since multiple windows of the same class may reside in the same application instance and share the same window procedure, those windows freely share all global data in the application's data segment. Therefore, a particular window should never depend on a global variable.

Consider a case in a text editing program based on the Windows multiple document interface (MDI) where each MDI child window maintains information about the file being edited in that window. If each window uses the same global variables to store page numbers, file names, and text length, only one window would display correct information. As soon as the user loads another file or performs an operation that changes any global variable, all windows are affected. The effects may not be apparent until a repaint occurs on the other windows, making the problem extremely difficult to track down. To avoid this situation, keep any window-specific data separately attached to each window class, isolating that data. Such data may be stored either in window extra bytes or property lists. The most efficient and fastest method is to allocate extra memory for data, and store that memory handle in window extra bytes. Property lists are most useful when dealing with preregistered window classes or with temporary data.

  1. Window Extra Bytes and Extra Allocations

An application specifies window extra bytes for a class in the cbWndExtra field of the WNDCLASS structure prior to calling the RegisterClass function. Windows allocates these bytes from USER's local heap in addition to the window's data structure. An application reads these extra bytes with the GetWindowWord and GetWindowLong functions and modifies them with the SetWindowWord and SetWindowLong functions.

Extra bytes are a very scarce resource because they reside in USER's local heap, and therefore reduce available system resources. Always minimize an application's use of window extra bytes as much as possible.

There is a myth that the GetWindowWord and GetWindowLong calls are extremely fast, which serves as an excuse to use a large number of window extra bytes. While these functions are highly optimized, they still need to perform necessary checks on the window handle and trap special cases. They do more than a simple lookup in USER's heap, and both require a far call. Only when an application requires a single WORD or DWORD should it store that data directly in extra bytes.

The most efficient method for allocating more than 4 extra bytes is to declare a single HANDLE in the window's extra bytes, allocate an additional data structure when creating the window, and store that handle in the window extra bytes. This method minimizes the use of extra bytes and provides very fast access to the entire data structure. If the data is allocated as local FIXED memory, there is no need to even lock the data before using it.

The best time to allocate this memory is while processing the WM_NCCREATE message; a window receives this message only once, very soon after creation. The code to perform this allocation might resemble the following:

   case WM_NCCREATE:
       pExtraData=(typecast)LocalAlloc(LPTR, cbData);
       SetWindowWord(hWnd, 0, (WORD)pExtraData);
       break;

Here, cbData is the number of bytes in the extra data structure, and the LPTR parameter to the LocalAlloc function specifies the LMEM_FIXED and LMEM_ZEROINIT flags. Use the number 0 as the index for the SetWindowWord call because 0 references the start of the extra bytes. Note also that (typecast) is a placeholder for the appropriate pointer type of the data structure.

The best time to free this memory is while processing the WM_NCDESTROY message. The code to perform this step might resemble the following:

    case WM_NCDESTROY:
        pExtraData=GetWindowWord(hWnd, 0);

        if ((typecast)NULL!=pExtraData)
            {
            //Clean out the old handle.
            SetWindowWord(hWnd, 0, 0);
            LocalFree((HANDLE)pExtraData);
            }
        break;


2.1 Using Extra Data in the Window Procedure

To access the extra data stored in separately allocated memory, a window must retrieve a pointer to the data before processing any message that requires that data.

If the application allocated memory as LMEM_FIXED, retrieving a pointer to the extra data structure requires only a single call to GetWindowWord, as follows:

   pExtraData=(typecast)GetWindowWord(hWnd, 0);

From this point on, accessing ANY field in the data structure is extremely fast because it requires only a dereference operation on the pointer. The following is a sample reference of the extra data:

   if (0==pExtraData->cMouseMoves)
       {
       ...
       }

The main advantage of this method over extended use of window extra bytes is that it uses only a single call to GetWindowWord and SetWindowWord functions to read and write ANY data in the entire data structure. Many calls to the GetWindowWord and SetWindowWord functions are necessary if this structure was stored entirely in extra bytes (unless the structure itself is only 2 bytes long).

In addition, maintaining the data structure is much easier when it is defined as a C structure and not as a large group of offsets for the GetWindowWord and SetWindowWord functions. Maintaining offsets requires carefully defining constants based on the sizes of the data types involved, and defining reasonably symbolic names to keep the source code readable. Defining a C structure is much easier and lets the C compiler determine the size of each field.

2.2 Local Versus Global Memory

Local memory from the application's heap is best suited for the purposes of instanced data, unless heap space is already a scarce commodity. Local allocations are very inexpensive as far as system memory is concerned. In addition, handles to local memory allocated with LMEM_FIXED are directly usable as near pointers.

In Windows version 3.x, each global allocation (through the GlobalAlloc function) uses a selector from the local descriptor table (LDT). Only 8192 are selectors available in Windows version 3.x enhanced mode and only 4096 are available in Windows 3.x standard mode. If an application creates hundreds of instances of a window that allocates a global segment, it will unnecessarily deplete the number of free selectors available to all applications in the system. For more information on using selectors, please query on the following words in the Microsoft Knowledge Base:

   prod(winsdk) and protect mode and handle limits

An application can conserve global handles by allocating a large block of memory and performing local allocations within the block. This technique, known as multiple FAR heaps, is described in Chapter 18 of the book titled "Windows 3.0 Power Programming Techniques" by Paul Yao and Peter Norton (Bantam Computer Books). Another good reference is the article "Improve Windows-Based App's Memory Use with Subsegment Allocation and Custom Resources" in the January 1991 issue of the "Microsoft Systems Journal" (volume 6, number 1).

  1. Properties

Properties allow an application to attach extra data to ANY window and do not require the application to process any messages for that window or modify the window's data in any way. An application attaches, retrieves, and deletes properties through the SetProp, GetProp, and RemoveProp functions, respectively.

Properties are most useful when an application needs to attach data to any of Windows's predefined classes, such as dialog boxes and controls, because such windows have no extra bytes available for application use. Properties are also very useful for temporary data that needs to be attached to a window for only a portion of that window's lifetime.

Using properties is relatively slow and has the added penalty that each property is limited to a WORD (which is very inconvenient for large structures). They also use space in the USER's local heap, and therefore consume valuable system resources. Follow the same guidelines for properties as for windows extra bytes, that is, allocate extra memory and store the handle as a single property.

If the application may use extensive properties for dialog boxes or standard Windows controls, consider one of the alternatives described below.

3.1 Alternatives to Property Lists for Dialog Boxes

If an application needs to attach data to a dialog box, consider using a private dialog class. The application registers this class and provides a window procedure similar to the way it would for any other type of application-specific window. A private dialog class requires at least DLGWINDOWEXTRA window extra bytes; the application must add DLGWINDOWEXTRA to any additional space for a memory handle. The extra memory can be allocated and freed using the same messages described in Section 2 above. Note that the offset of the memory handle for the GetWindowWord and SetWindowWord functions is DLGWINDOWEXTRA instead of 0 (zero).

For more information on private dialog classes, please query on the following words in the Microsoft Knowledge Base:

   prod(winsdk) and defdlgproc

3.2 Alternatives to Property Lists for Predefined Control Windows

Most of Windows's predefined controls already use some number of window extra bytes however they do not reserve any space for application-specific use. An application can, however, create a superclass and specify space for a HANDLE to extra data.

Superclassing is the process of retrieving a window's class information, modifying the appropriate fields, and registering the new class under a different name. The following six steps are involved in this process:

  1. Call the GetClassInfo function using the hWnd of an existing window of the class to superclass. This fills a WNDCLASS structure with information for that class.

  2. Change the hInstance field of the WNDCLASS structure to the hInstance of the application.

  3. Save the old lpfnWndProc value and store the address of the superclass procedure in that field. Class extra bytes are a suitable place to save the old value.

    Pass all messages not processed by the superclass procedure to this original lpfnWndProc and be sure to export the superclass procedure.

  4. Save the value of cbWndExtra (again, class extra bytes are suitable for this purpose) and add sizeof(HANDLE) to that field. Do NOT simply store sizeof(HANDLE) or the control will break. The superclass MUST use the existing value of cbWndExtra as the offset to GetWindowWord and SetWindowWord functions when manipulating the extra memory handle.

  5. Change the class name to something unique. Windows will not reregister a class if one already exists by that name.

  6. Call the RegisterClass function with the new WNDCLASS structure.

Allocate extra memory for windows of this superclass using the same method described above in Section 2.


Additional reference words: 3.00 3.10
KBCategory: kbprg
KBSubcategory: UsrWndw
Keywords : kb16bitonly


THE INFORMATION PROVIDED IN THE MICROSOFT KNOWLEDGE BASE IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND. MICROSOFT DISCLAIMS ALL WARRANTIES, EITHER EXPRESS OR IMPLIED, INCLUDING THE WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT SHALL MICROSOFT CORPORATION OR ITS SUPPLIERS BE LIABLE FOR ANY DAMAGES WHATSOEVER INCLUDING DIRECT, INDIRECT, INCIDENTAL, CONSEQUENTIAL, LOSS OF BUSINESS PROFITS OR SPECIAL DAMAGES, EVEN IF MICROSOFT CORPORATION OR ITS SUPPLIERS HAVE BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. SOME STATES DO NOT ALLOW THE EXCLUSION OR LIMITATION OF LIABILITY FOR CONSEQUENTIAL OR INCIDENTAL DAMAGES SO THE FOREGOING LIMITATION MAY NOT APPLY.

Last reviewed: July 23, 1997
© 1998 Microsoft Corporation. All rights reserved. Terms of Use.