Andrew Truckle
Andrew Truckle

Reputation: 19157

Inserting and item into CComboBoxEx and ReleaseBuffer

Code:

m_cbReminderInterval.ResetContent();
for (int i = 1; i <= m_iMaxReminderInterval; i++)
{
    COMBOBOXEXITEM cmbItem = {};
    CString strNumber;
    strNumber.Format(_T("%d"), i);

    cmbItem.mask = CBEIF_TEXT;
    cmbItem.iItem = static_cast<INT_PTR>(i) - 1;
    cmbItem.pszText = strNumber.GetBuffer(_MAX_PATH);
    strNumber.ReleaseBuffer(); // TODO: When should I release the buffer - NOW or AFTER the InsertItem call?

    m_cbReminderInterval.InsertItem(&cmbItem);
}

My question is:

Is it better to use GetString instead of GetBuffer in this context? The only issue I see is that pszText is LPWSTR whereas GetString returns LPCWSTR. If I should continue to use GetBuffer then when should it actually be released? Before or after the InsertItem call?

Upvotes: 2

Views: 149

Answers (2)

IInspectable
IInspectable

Reputation: 51479

There's a common pattern in the Windows API you'll see over and over again: Structures that are less const-correct than what would appear to be possible. Undoubtedly, some of them are oversights, but not this one: COMBOBOXEXITEM is used both to insert and query an item's data.

This is hinted to, in part, in the documentation for the pszText member:

A pointer to a character buffer that contains or receives the item's text. If text information is being retrieved, this member must be set to the address of a character buffer that will receive the text.

The second part of the contract is omitted from the documentation, sadly. When setting an item's text, the control makes a copy of the string passed in, and neither takes ownership over the pointed to data, nor modifies it. In other words: When using the COMBOBOXEXITEM structure to insert an item, all pointers can be assumed to point to const.

Following that, it is perfectly valid to pass the pointer received from GetString():

for (int i = 1; i <= m_iMaxReminderInterval; i++)
{
    COMBOBOXEXITEM cmbItem = {};
    CString strNumber;
    strNumber.Format(_T("%d"), i);

    cmbItem.mask = CBEIF_TEXT;
    cmbItem.iItem = static_cast<INT_PTR>(i) - 1;
    cmbItem.pszText = const_cast<TCHAR*>(strNumber.GetString());

    m_cbReminderInterval.InsertItem(&cmbItem);
}

Upvotes: 4

Vlad Feinstein
Vlad Feinstein

Reputation: 11311

According to CSimpleStringT::GetBuffer:

If you use the pointer returned by GetBuffer to change the string contents, you must call ReleaseBuffer before you use any other CSimpleStringT member methods.

You are not modifying the string, so you don't need to call ReleaseBuffer.

But as you said, it's better to use GetString, at least you indicate your intent to NOT modify it.

Upvotes: 1

Related Questions