SMS

The SMS (formally tagSMS) structure is how WIN32K.SYS—and before it, WINSRV.DLL—packages a message that is to be sent between threads (which many programmers will think of as between windows).

Variability

The following changes of size are known.

Version Size (x86) Size (x64)
3.10 0x40  
3.51 to 6.3 0x3C 0x70
10.0 0x48 0x88

Layout

Offset (x86) Offset (x64) Definition Versions
0x00 0x00
SMS *psmsNext;
3.10 to 6.3
unknown LIST_ENTRY 10.0 and higher
0x04 (3.10)   unknown SMS pointer 3.10 only
0x08 (3.10)   unknown SMS pointer 3.10 only
0x0C (3.10);
0x04 (3.51 to 6.3);
0x08
0x08 (5.2 to 6.3);
0x10
SMS *psmsReceiveNext;
3.10 to 6.3  
unknown LIST_ENTRY 10.0 and higher

Each newly allocated SMS is inserted at the head of a global single-linked list, linking through the psmsNext pointer. Version 10.0 makes this a double-linked list.

Each newly allocated SMS is also inserted at the tail of the receiving thread’s pmsReceiveList, linking through the psmsReceiveNext pointer. Again, version 10.0 changes this list from single-linked to double. While a thread receives messages, it drains from the head of its psmsReceiveList.

Offset (x86) Offset (x64) Definition Versions Remarks
0x10 (3.10);
0x08 (3.51 to 5.1)
 
ULONG tSent;
3.10 to 5.1 next at 0x20
0x14 (3.10);
0x0C (3.51 to 5.1);
0x08 (5.2 to 6.3);
0x10
0x10 (5.2 to 6.3);
0x20
THREADINFO *ptiSender;
all  
0x18 (3.10);
0x10 (3.51 to 5.1);
0x0C (5.2 to 6.3);
0x14
0x18 (5.2 to 6.3);
0x28
THREADINFO *ptiReceiver;
all  
0x1C (3.10);
0x14 (3.51 to 5.1);
0x10 (5.2 to 6.3);
0x18
0x20 (5.2 to 6.3);
0x30
SENDASYNCPROC lpResultCallBack;
all  
0x20 (3.10);
0x18 (3.51 to 5.1);
0x14 (5.2 to 6.3);
0x1C
0x28 (5.2 to 6.3);
0x38
DWORD_PTR dwData;
all  
0x24 (3.10);
0x1C (3.51 to 5.1);
0x18 (5.2 to 6.3);
0x20
0x30 (5.2 to 6.3);
0x40
THREADINFO *ptiCallBackSender;
all  
0x28 (3.10);
0x20 (3.51 to 5.1);
0x1C (5.2 to 6.3);
0x24
0x38 (5.2 to 6.3);
0x48
LONG_PTR lRet;
all  
0x20 (5.2 to 6.3);
0x28
0x40 (5.2 to 6.3);
0x50
ULONG tSent;
5.2 and higher previously at 0x08
0x2C (3.10);
0x24 (3.51 to 6.3);
0x2C
0x44 (5.2 to 6.3);
0x54
UINT flags;
all  
0x30 (3.10);
0x28 (3.51 to 6.3);
0x30
0x48 (5.2 to 6.3);
0x58
WPARAM wParam;
all  
0x34 (3.10);
0x2C (3.51 to 6.3);
0x34
0x50 (5.2 to 6.3);
0x60
LPARAM lParam;
all  
0x38 (3.10);
0x30 (3.51 to 6.3);
0x38
0x58 (5.2 to 6.3);
0x68
UINT message;
all  
0x3C (3.10);
0x34 (3.51 to 6.3);
0x3C
0x60 (5.2 to 6.3);
0x70
WND *spwnd;
all last member in 3.10
0x38 (3.51 to 6.3);
0x40
0x68 (5.2 to 6.3);
0x78
PVOID pvCapture;
3.51 and higher last member in 3.51 to 6.3
0x44 0x80 unknown dword 10.0 and higher last member in 10.0

Flags

Debugger extensions from DDKs for Windows NT 3.51 through Windows 2000 helpfully have descriptive strings for the bits within the flags. It seems highly plausible that these are the macros that are used for the bits in the source code.

Mask Name Versions
0x00000001 SMF_REPLY all
0x00000002 SMF_RECEIVERDIED all
0x00000004 SMF_SENDERDIED all
0x00000008 SMF_RECEIVERFREE all
0x00000010 SMF_RECEIVERBUSY 3.10 to 3.51
SMF_RECEIVEDMESSAGE 4.0 and higher
0x000000E0 perhaps unused  
0x00000100 SMF_CB_REQUEST all
0x00000200 SMF_CB_REPLY all
0x00000400 SMF_CB_CLIENT all
0x00000800 SMF_CB_SERVER all
0x00001000 SMF_WOWRECEIVE 3.51 and higher
0x00002000 SMF_WOWSEND 3.51 and higher
0x00004000 SMF_RECEIVEDMESSAGE 3.51 only
SMF_RECEIVERBUSY 4.0 and higher
0x00008000   6.0 and higher
0x00010000   10.0 and higher

There seems to have been some confusion over the SMF_RECEIVERBUSY and SMF_RECEIVEDMESSAGE flags. Version 3.10 has only whichever one of them is the 0x00000010 flag. It is set on receiving the message. Version 3.51 introduces the other flag, 0x00004000. This too is set on receiving the message, but it gets cleared before either freeing the message or replying to it.

By the way, the SMF_RECEIVERFREE flag is not named in contrast to SMF_RECEIVERBUSY. It simply means that the receiving thread is to free the SMS, notably because the sending thread wants no reply.