10.1.4 The Scope of DLL Data: Multiprocess, Process, and Thread

This section will discuss the scope of variables and dynamically allocated memory in a DLL. A DLL can have data, variables, and allocated memory that are shared by multiple processes that attach to it. It is also possible to have data, variables, and allocated memory that are private to each attached process. Finally, you can store data on a per thread basis to allow multiple instances of a thread each to have its own private data.

The default behavior for the variables of a DLL is the same as in any statically linked code. Local variables in a DLL function are limited to the scope of the function in which they are declared. Global variables in a DLL source code file are global to each process that attaches to the DLL. Static variables create permanent storage for each attached process, with scope limited to the block in which they are declared. This means that by default, each process has its own instance of global and static variables.

When a DLL allocates memory using any of the allocation functions (GlobalAlloc, LocalAlloc, HeapAlloc, VirtualAlloc, or malloc and other C-Runtime allocation functions), the memory is allocated in the address space of the calling process and is accessible only to the threads of that process. The only exception to this is memory allocated using GlobalAlloc with the GMEM_SHARE option. The recommended way to allocate memory that can be shared between processes, is to use file mapping. Refer to Chapter 7, “File Mapping” for a general discussion of using file mapping to create named shared memory; and to Section 0.2.4, “The LibMain Function” for an example of using the LibMain function of a DLL to set up shared memory using file mapping.

For a DLL to store a separate instance of a block of data for each thread that attaches to it, you would use the Thread Local Storage (Tls . . . ) functions. This would allow the DLL to support a process that creates multiple instances of a thread. For example, a spreadsheet application might create a new instance of the same thread each time the user opens a new spreadsheet. A DLL providing the functions for various spreadsheet operations could use Thread Local Storage to save information about the current state of each spreadsheet (row, column, etc.). As an alternative to using the Thread Local Storage functions of the Win32 API, the Microsoft compiler supports the use of the thread keyword. This is used when a variable is declared, to indicate that each thread that calls the function or module gets a separate instance of the variable.

The Microsoft Windows NT Linker allows you to use a DLL's module definition file to change the default scope of global and static variables. Module definition files do not affect the scope of dynamically allocated memory. For additional information refer to the documentation for the tools that you are using. The Microsoft Linker supports the use of the DATA and SECTIONS statements. The DATA statement sets the default data attributes for all sections of the DLL. The data attributes that can be set are READ, WRITE, EXECUTE, and SHARED, where the defaults are READ and WRITE. If the DLL's module definition file specifies:

DATA READ WRITE SHARED

all the global and static variables of the DLL will be shared by all processes that attach to it. The SECTIONS statement allows you to set the attributes for a specific section of a DLL. For example, this can be used to make the initialized data shared while the uninitialized variables are not. You can also use more than one source code file to create separate segments whose data attributes can be set independently.

It should be noted that any shared DLL memory persists only as long as there is a process attached it. To create persistent shared memory you would need a background server process.