en:docs:win16:modules:local_heap

Differences

This shows you the differences between two versions of the page.

Link to this comparison view

Both sides previous revisionPrevious revision
Next revision
Previous revision
en:docs:win16:modules:local_heap [2026/02/24 03:18] – [LocalInfo Structure (386)] prokusheven:docs:win16:modules:local_heap [2026/02/24 08:12] (current) – [References] prokushev
Line 1: Line 1:
-====== Win16 Local Heap Functions ======+===== Local Heap and Atom Table =====
  
 ===== Overview ===== ===== Overview =====
Line 16: Line 16:
 | 00h | WORD | wMustBeZero | Must be zero for the NULL segment structure to be considered present. | | 00h | WORD | wMustBeZero | Must be zero for the NULL segment structure to be considered present. |
 | 02h | DWORD | dwOldSSSP | When SwitchStackTo() is called, the current SS:SP is stored here. At other times, may contain the value 5 (from C compiler's _rsrvptrs). | | 02h | DWORD | dwOldSSSP | When SwitchStackTo() is called, the current SS:SP is stored here. At other times, may contain the value 5 (from C compiler's _rsrvptrs). |
-| 06h | WORD | pLocalHeap | Near pointer to the Local Heap information structure (i.e., the HeapInfo structure). This field is set by LocalInit() and points to the beginning of the local heap management structures. If no local heap exists, this field may be stale (non-zero but invalid). Always verify the heap by checking the signature at offset 22h (Standard mode) or 28h (KRNL386) – see li_sig below. | +**06h** **WORD** **pLocalHeap** | Near pointer to the Local Heap information structure (i.e., the HeapInfo structure). This field is set by LocalInit() and points to the beginning of the local heap management structures. If no local heap exists, this field may be stale (non-zero but invalid). Always verify the heap by checking the signature at offset 22h (Standard mode) or 28h (KRNL386) – see li_sig below. | 
-| 08h | WORD | pAtomTable | Near pointer to the atom table, set by InitAtomTable(). Zero until atoms are used. |+**08h** **WORD** **pAtomTable** | Near pointer to the atom table structure. Set by InitAtomTable(). Zero until atoms are used. |
 | 0Ah | WORD | pStackTop | Near pointer to the end (top) of the stack. For DLLs, this is zero. | | 0Ah | WORD | pStackTop | Near pointer to the end (top) of the stack. For DLLs, this is zero. |
 | 0Ch | WORD | pStackMin | High‑water mark of stack usage. For DLLs, zero. | | 0Ch | WORD | pStackMin | High‑water mark of stack usage. For DLLs, zero. |
Line 79: Line 79:
 | 26h | WORD | li_minsize | Minimum size of the local heap, as specified by the HEAPSIZE line in the .DEF file. | | 26h | WORD | li_minsize | Minimum size of the local heap, as specified by the HEAPSIZE line in the .DEF file. |
 | 28h | WORD | li_sig | Signature word set to 484Ch ('LH' in a hex dump). Used by various Windows routines to verify heap integrity. This signature should be checked when validating a potential heap pointer from offset 6. | | 28h | WORD | li_sig | Signature word set to 484Ch ('LH' in a hex dump). Used by various Windows routines to verify heap integrity. This signature should be checked when validating a potential heap pointer from offset 6. |
- 
- 
  
 ===== LocalInfo Structure (286) ===== ===== LocalInfo Structure (286) =====
Line 155: Line 153:
   * **Locking (LocalLock/LocalUnlock)** manipulates the lhe_count field of the handle entry for MOVEABLE blocks; for FIXED blocks, no count is maintained.   * **Locking (LocalLock/LocalUnlock)** manipulates the lhe_count field of the handle entry for MOVEABLE blocks; for FIXED blocks, no count is maintained.
   * **Discarding (LocalDiscard)** frees the memory of a MOVEABLE block but keeps the handle entry alive with the LHE_DISCARDED flag set.   * **Discarding (LocalDiscard)** frees the memory of a MOVEABLE block but keeps the handle entry alive with the LHE_DISCARDED flag set.
 +
 +===== Atom Tables =====
 +
 +Atom tables provide a mechanism for efficiently storing and sharing strings (atoms) in 16-bit Windows. Each module may have its own local atom table located in its local heap. In addition, there is a single global atom table that serves the entire system.
 +
 +The field at offset **08h** in the Instance Data (`pAtomTable`) contains a near pointer to the atom table header for the current module. This pointer is initialized by calling `InitAtomTable()`. If no atom table exists, `pAtomTable` is zero.
 +
 +==== Relationship with the Local Heap ====
 +
 +Physically, an atom table resides **inside** the local heap of some data segment. Therefore, before creating an atom table, the segment must be initialized as a local heap by calling `LocalInit()`.
 +
 +==== ATOMENTRY Structure ====
 +
 +Each string atom is stored as an `ATOMENTRY` structure in the local heap. The structure has the following form:
 +
 +^ Offset ^ Type ^ Field ^ Description ^
 +| 00h | WORD | `next` | Next entry in the same hash bucket (0 if last). |
 +| 02h | WORD | `usage` | Reference count. |
 +| 04h | BYTE | `len` | Length of the string (1–255). |
 +| 05h | BYTE[] | `name` | ASCIIZ string (length `len` + 1). |
 +
 +==== Types of Atoms ====
 +
 +Windows supports two fundamentally different types of atoms: **string atoms** and **integer atoms**. Their handling is completely distinct.
 +
 +===== String Atoms =====
 +
 +String atoms are created by passing an ordinary string to `AddAtom` or `GlobalAddAtom`. They are stored in the atom table as `ATOMENTRY` structures.
 +
 +  * **Range**: `0xC000` to `0xFFFF` (encoded pointer).
 +  * **Storage**: Allocated in the local heap as `ATOMENTRY`, inserted into the hash table.
 +  * **Reference count**: Yes (`usage` field).
 +  * **String representation**: The original string.
 +  * **Creation**: `AddAtom("MyString")`.
 +
 +**Encoding:** A string atom value is derived from the near pointer to its `ATOMENTRY`. Since the pointer is 4‑byte aligned, the low two bits are zero. The atom is formed by shifting the pointer right by 2 bits and ORing with `0xC000`. This guarantees the range `0xC000–0xFFFF`.
 +
 +<code c>
 +#define HANDLETOATOM(handle) ((ATOM)(0xc000 | ((handle) >> 2)))
 +#define ATOMTOHANDLE(atom)   ((HANDLE16)(atom) << 2)
 +</code>
 +
 +===== Integer Atoms =====
 +
 +Integer atoms are created by passing a string of the form `"#dddd"` (or by using `MAKEINTATOM` with a value ≤ 0xBFFF). They are **not stored in the atom table** and have no associated `ATOMENTRY` structure.
 +
 +  * **Range**: `0x0001` to `0xBFFF` .
 +  * **Storage**: None; the value is used directly as the atom.
 +  * **Reference count**: Not applicable.
 +  * **String representation**: Generated on the fly as `"#dddd"` when `GetAtomName` is called.
 +  * **Creation**: `AddAtom("#1234")` or `AddAtom(MAKEINTATOM(0x04D2))`.
 +
 +**How it works:** When a string of the form '#dddd' is passed, the function parses the decimal number and, if it is less than 0xC000, returns it directly without accessing the atom table. Similarly, `FindAtom` for such a string or for a `MAKEINTATOM` value simply returns the number without any lookup. Integer atoms are always considered "found" because any value in the range is valid.
 +
 +==== MAKEINTATOM Macro ====
 +
 +The **`MAKEINTATOM`** macro is defined as:
 +
 +<code c>
 +#define MAKEINTATOM(i)  (LPTSTR)((DWORD)((WORD)(i)))
 +</code>
 +
 +This macro casts a 16‑bit integer value to a pointer type. When this "pointer" is passed to atom functions, it is interpreted as an integer atom (if the value is ≤ `0xBFFF`) or as a string atom (if ≥ `0xC000`).
 +
 +  * For values ≤ `0xBFFF`, the function treats it as an integer atom and returns the value directly.
 +  * For values ≥ `0xC000`, the function assumes it is an encoded pointer to an `ATOMENTRY` and will dereference it (after shifting left by 2 bits) to access the atom entry.
 +
 +**Important:** `MAKEINTATOM` does not create a string or allocate any memory; it is simply a type-punning convenience to pass integer atoms to functions that formally expect a string pointer.
 +
 +==== Local vs. Global Atom Tables ====
 +
 +  * **Local atom tables**: Bound to a specific data segment (e.g., an application's DGROUP). Created by calling `InitAtomTable()`. Used for a module's internal needs. Access is only possible when the DS register points to that segment.
 +  * **Global atom table**: A system-wide table accessible to all applications via `GlobalAddAtom`, `GlobalFindAtom`, and `GlobalDeleteAtom` . Physically, it resides not in an application's data segment but in a special USER data segment (part of the so-called "global atom and text heap") . Its structure is identical to a local atom table. The `Global...` functions internally switch DS to the USER segment and call the ordinary `AddAtom`/`FindAtom`.
 +
 +==== Creating Custom Atom Tables (outside DGROUP) ====
 +
 +Since all atom operations work with the current segment pointed to by DS, you can create and use an atom table in any arbitrary data segment by following three steps:
 +
 +  -  **Create a local heap** in the target segment using `LocalInit(Selector, Start, End)`.
 +  - **Switch the DS register** to that segment.
 +  - Call `InitAtomTable(size)` to initialize the atom table in the newly created heap.
 +
 +After that, any subsequent call to `AddAtom`, `FindAtom`, etc., will operate on the custom table if DS is temporarily set to the correct segment.
 +
 +==== Summary of Atom Type Differences ====
 +
 +^ Feature ^ String Atoms ^ Integer Atoms ^
 +| Range | `0xC000` – `0xFFFF` | `0x0001` – `0xBFFF` |
 +| Stored in atom table | Yes, as `ATOMENTRY` in hash buckets | No |
 +| Memory allocated | `ATOMENTRY` structure in local heap | None |
 +| Reference count | Yes (`usage`) | No |
 +| String representation | Original string | Generated as `"#dddd"` on the fly |
 +| Creation | `AddAtom("MyString")` | `AddAtom("#1234")` or `AddAtom(MAKEINTATOM(0x04D2))` |
 +| Find behavior | Searches hash table | Always returns the value (always "found") |
 +| Delete behavior | Decrements refcount, frees if zero | No operation (returns 0) |
 +
 +===== Custom Local Heaps =====
 +
 +Although each module typically has a default local heap in its DGROUP, it is possible to create additional local heaps in other segments or in globally allocated memory. This is useful for managing private memory pools within a large global block, or for creating atom tables in separate memory areas.
 +
 +==== Using LocalInit() to Create a Custom Local Heap ====
 +
 +The `LocalInit()` function initializes a local heap within a specified segment. Its prototype is:
 +
 +<code c>
 +WORD LocalInit(WORD wSegment, WORD pStart, WORD pEnd);
 +</code>
 +
 +  * `wSegment` – Selector of the segment where the heap will be created.
 +  * `pStart` – Offset of the first byte of the heap area (must be paragraph‑aligned, i.e., a multiple of 16).
 +  * `pEnd` – Offset of the last byte of the heap area (inclusive). The heap will manage memory from `pStart` to `pEnd`.
 +
 +If successful, `LocalInit()` returns a non‑zero value. It sets up the `HeapInfo` and `LocalInfo` structures at the beginning of the heap area (starting at `pStart`) and updates the segment’s instance data at offset **06h** (`pLocalHeap`) to point to that `HeapInfo` structure. However, if the segment is not a default data segment (i.e., not DGROUP), the instance data at offset 0 must also contain a zero word to indicate that the NULL segment structure is present; otherwise, the heap may not be recognized by some routines.
 +
 +**Example:** Creating a local heap in a globally allocated block of memory (64 KB):
 +
 +<code asm>
 +; 1. Allocate a 64KB global memory block
 +GlobalAlloc GMEM_FIXED, 0x10000
 +mov dx, ax          ; DX = selector of allocated block
 +
 +; 2. Temporarily set DS to that segment to access its instance data
 +push ds
 +push dx
 +pop ds
 +
 +; 3. Initialize the NULL segment (Instance Data) at offset 0.
 +;    The first word must be zero (wMustBeZero = 0).
 +xor ax, ax
 +mov word ptr [0], ax
 +; The other fields (pLocalHeap, etc.) will be filled by LocalInit.
 +
 +; 4. Define the heap area: start at offset 16 (0x0010) to preserve
 +;    the 16-byte Instance Data, end at 0xFFFF (the last byte of the segment).
 +mov bx, 16          ; pStart = 16
 +mov cx, 0xFFFF      ; pEnd = 0xFFFF
 +
 +; 5. Restore DS (if no longer needed)
 +pop ds
 +
 +; 6. Call LocalInit to create the heap in segment dx, from pStart to pEnd.
 +push dx
 +push bx
 +push cx
 +call LocalInit      ; returns non-zero on success
 +
 +; After the call, the Instance Data at offset 6 in segment dx
 +; contains a valid near pointer to the HeapInfo structure located at offset 16.
 +</code>
 +
 +After this call, the global block can be used with local heap functions (`LocalAlloc`, `LocalFree`, etc.) by using the selector in `DX` and near pointers (offsets) within that segment.
 +
 +**Important Considerations:**
 +
 +  * The heap structures themselves occupy space at the beginning of the heap area. The first block (sentinel) resides at `pStart + size of (LocalInfo)`.
 +  * The segment’s instance data (at offset 0) must be properly set up, especially the zero word at offset 0, to avoid confusion with other structures.
 +  * Custom local heaps are not automatically enlarged if they run out of space; they are limited to the range specified in `LocalInit`.
 +  * The `HEAPSIZE` setting in the module’s .DEF file only affects the default DGROUP heap.
 +
 +==== Creating Atom Tables Outside DGROUP ====
 +
 +As already described, to create an atom table in an arbitrary memory area, you must first initialize a local heap there (as above) and then, after switching DS, call `InitAtomTable`. This technique allows fully isolated atom tables for special purposes.
 +
 +==== Summary of Custom Heap and Atom Table Creation ====
 +
 +  * Use `LocalInit` on a segment to establish a local heap anywhere in memory.
 +  * The segment must have a valid NULL segment structure (zero word at offset 0) for the heap to be recognized.
 +  * After `LocalInit`, you can use `LocalAlloc`, `LocalLock`, etc., with near pointers within that segment.
 +  * To create an atom table in a custom heap, switch DS to that segment and call `InitAtomTable`.
 +  * All subsequent atom operations must be performed with DS set appropriately (or via wrapper functions).
 +  * Custom heaps and atom tables are useful for isolating memory pools, implementing resource managers, or working with large data structures without polluting the default DGROUP.
 +
 +===== References =====
 +
 +  - Schulman, A., Maxey, D., Pietrek, M. //Undocumented Windows//. Addison-Wesley, 1992.
 +  - Pietrek, M. //Windows Internals//. Addison-Wesley, 1993.
 +  - Chen, R. //The Old New Thing// (blog). Microsoft Developer Blogs.
 +  - Microsoft OS/2 Version 1.1 Programmer's Reference, Volume 1.