INF: Guidelines for Allocating Instance (Per-Window) Data

ID Number: Q76103

1.00 2.03 2.10 3.00 3.10

WINDOWS

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.

2. 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 Application Memory Use

with Subsegment Allocation and Custom Resources" in the January 1991

issue of the "Microsoft Systems Journal" (volume 6, number 1).

3. 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 1.x 2.x 3.x instance control