Why use a message mask?
As you can probably guess, a list of messages subclassed by each object must be kept. During the subclass callback, the subclasser must enumerate through all objects which have subclassed the given window, and check the current message to see if it has been requested. If so, then make the callback. If not, then move on to the next object or exit the procedure. The easiest way to accomplish this would be to keep an array of message values for each object that subclasses the window. However, this would be inefficient on two fronts. First, it would require duplicate values in each array if two or more objects subclassed the same message. Second, it would require iterating through one array for each object that subclasses a window during the callback, when performance is critical. Using a message mask solves both of these issues.
OK, but how does it work?
Instead of an array of message values being kept for each object that subclasses a window, an array is kept for each window that is subclassed. The messages are then shared between all objects which have subclassed that window. In order to tell which objects have requested which messages, a message mask is kept for each object. A message mask is nothing more than an array of 32 bit values used as bitmasks. If an object has requested the first message in the table, then the first bit of the first index will be 1. If an object has requested the 78th message in the message table, the 14th bit in the 3rd index will be 1.
For example, assume that Object A and Object B have both subclassed 10 messages on Window A. 5 of these messages are the same, and 5 are not, making 15 unique messages. What you end up with could look like this:
# | Main Message Table | Object A | Object B |
1 | WM_CREATE | WM_CREATE | |
2 | WM_DESTROY | WM_DESTROY | WM_DESTROY |
3 | WM_MOVE | WM_MOVE | WM_MOVE |
4 | WM_SIZE | WM_SIZE | |
5 | WM_ACTIVATE | WM_ACTIVATE | |
6 | WM_SETFOCUS | WM_SETFOCUS | |
7 | WM_KILLFOCUS | WM_KILLFOCUS | |
8 | WM_ENABLE | WM_ENABLE | WM_ENABLE |
9 | WM_SETREDRAW | WM_SETREDRAW | WM_SETREDRAW |
10 | WM_SETTEXT | WM_SETTEXT | |
11 | WM_GETTEXTLENGTH | WM_GETTEXTLENGTH | |
12 | WM_PAINT | WM_PAINT | WM_PAINT |
13 | WM_CLOSE | WM_CLOSE | |
14 | WM_QUIT | WM_QUIT | |
15 | WM_CLOSE | WM_CLOSE |
With fewer than 32 unique messages requested, the message mask would only use the first 32 bit index, and the values would be (Object A) &HFAF& and (Object B) &H79D6&.
What are the limitations of this design?
The message masks are store in static arrays. While this provides excellent speed, it also imposes a strict limit on the number of unique messages that can be subclassed for each window. The maximum number of unique messages available to be subclassed is 32 * (the number of indexes in the message mask array). The default number of indexes is 4, which provides minimal memory usage, but limits unique messages to 128. It is important to realize that this applies to *unique* messages. If you have one object which subclasses 120 messages, you can create an unlimited number of instances of that object, even subclassing the same window, with no problems. If you need to subclass more than 128 unique messages on a single window, adjust the MsgMaskCount constant in the pcSubclassHub.cls module. For example, if this value was changed to 8, then 256 unique messages would be available, and 4 extra bytes would be allocated for every subclass created by every object, regardless of whether the extra message space was utilized or not.
Where can I go to start fooling with the code?
The message masks are maintained by pcSubclassHub.cls. As the name implies, this class is responsible for keeping track of which objects are requesting which messages and relaying them appropriately.