Geoff Chappell, Software Analyst
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).
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 |
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 |
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.