다음을 통해 공유


on 64 bit data conversion, comctl32 and reading the documentation

Question: What is wrong with this code?

case WM_DRAWITEM:
{
LPDRAWITEMSTRUCT pdis = (LPDRAWITEMSTRUCT) lParam;
   COMBOBOXEXITEM cbexItem = {0};
cbexItem.mask = CBEIF_IMAGE | CBEIF_SELECTEDIMAGE;
   cbexItem.iItem = pdis->itemID;

   CallWindowProc(pfnOldWndProc, hwnd, CBEM_GETITEM, 0, (LPARAM)&cbexItem);
...
}

Answer: It will break on 64 bit machines.

This is just one example of a well-known issue.  ComboBoxEx makes frequent use of -1 when asking for the item in the box.  Notice the item we ask the ComboBoxEx for comes from the itemID member of the DRAWITEMSTRUCT.  This seems like a perfectly normal thing to do.  However, when using data structures such as the ones commonly defined throughout Win32 and ComCtl, data structures that you the programmer did not define, it is important to look closely at the data types in the documentation.

DRAWITEMSTRUCT's itemID member is a UINT, while COMBOBOXEXITEM's iItem member is an INT_PTR.  So what happens when -1 is assigned to itemID then you assign that value to iItem?

Well, the UINT value is a 32 bit number on 64 bit machines and will have a value of 0xFFFFFFFF when comctl32 assigns -1.  Later, when this is assigned to the INT_PTR value, which is a 64 bit value on 64 bit machines, it will have a value of 0x00000000'FFFFFFFF.  This value is handed back to comctl32 in the form of the CBEM_GETITEM message.  When comctl32 does a comparison of the INT_PTR value against -1, it will be comparing against 0xFFFFFFFF'FFFFFFFF, and the comparison will fail.  Since your combo box probably doesn't have 4294967295 items in it, the CBEM_GETITEM call will fail to return useful information. The corrected code is:

cbexItem.iItem = (INT)pdis->itemID;

The moral of this story, in case you missed it, was know your data types and think carefully about what will happen in the 64 bit case when performing assignments that imply casts.