1

My parent dialog has a CComboBoxEx control (which is mapped to a derived class called CDatesComboBoxEx).

In one part of the application this dialog displays a popup modal dialog. And, inside the modal dialog, it needs access to information from the dates combo.

What I decided to do (which works fine) is pass the address of my combo in the constructor of the popup dialog. So I can now do things like:

  • m_pComboDates->GetCount()
  • m_pComboDates->GetItemDataPtr(i)

I was wondering if there was any way to use native Win32 code here instead?

  • We can get access to the parents handle (GetParent()->GetSafeHWnd()).
  • We know the ID of the control on the parent dialog (IDC_COMBOBOXEX_OCLM_WEEK_OF_MEETING).

So is it possible to somehow directly get the count and item data?


I know that there are these macros:

But:

  1. Can these be macros be used with CComboBoxEx control? And ...
  2. How do we get the HWND on the combo given the context I previously described?

Actually, I think I missunderstood the purpose of those "macros". I can get the combo handle like this:

HWND hDatesCombo = ::GetDlgItem(
    GetParent()->GetSafeHwnd(), IDC_COMBOBOXEX_OCLM_WEEK_OF_MEETING);

But, ComboBox_GetCount does not return a value. Nor the others. So I am somewhat confused.


Based on the answer, this bit is now fine:

HWND hDatesCombo = ::GetDlgItem(GetParent()->GetSafeHwnd(), IDC_COMBOBOXEX_OCLM_WEEK_OF_MEETING);
int iNumDates = static_cast<int>(::SendMessage(hDatesCombo, CB_GETCOUNT, 0, 0));

And inside my for loop I am doing this:

LRESULT itemData = ::SendMessage(hDatesCombo, CB_GETITEMDATA, static_cast<WPARAM>(i), 0);
auto* pEntry = static_cast<CChristianLifeMinistryEntry*>((LPVOID)itemData);

That is the only way I can find to cast it. If I try static_cast<LPVOID> it won't work either.

Andrew Truckle
  • 17,769
  • 16
  • 66
  • 164
  • 5
    `ComboBox_GetCount` returns a value. It's the documentation that's broken. It's simply what you get when you trust .NET developers to generate the native platform API documentation. The documentation for **every single preprocessor macro** is broken to the point of being useless. Thank you very much, Microsoft. – IInspectable Oct 02 '21 at 16:35
  • 1
    Try `GetOwner()` instead of `GetParent()`. Also, did you pass the main dialog in the popup dialog's constructor? `CPopupMyDlg dlg(this)` ComboBoxEx is derived from ComboBox, so all functions/messages/macros should work the same. The "purpose" of those macros is just convenience, they simply send the proper message. – Constantine Georgiou Oct 02 '21 at 16:59
  • @ConstantineGeorgiou I do pass `this` so that is OK. The issue was trying to call my test code in the popup window constructor rather than the actual right part of the popup window class (ie. `OnInitDialog` needs to have been processed before we can call this code). – Andrew Truckle Oct 02 '21 at 17:09
  • Hmm, this may have to do with the initialization, sequence of events etc etc of the MFC classes. Inside `OnInitDialog()` the window (dialog) as well as its children are already created. I would try calling the WinAPI `::GetParent()` instead of the MFC one. I think this would work, even in `OnInitDialog()`. – Constantine Georgiou Oct 02 '21 at 17:28
  • @ConstantineGeorgiou Using the existing code **did** work in `OnInitDialog`. It just failed when I tried in the **constructor**. – Andrew Truckle Oct 02 '21 at 17:29
  • 1
    On your last bit: Why are you combining C-style casts with C++ casts? No, you can't use `static_cast` to get from an `LRESULT` to a pointer, but you *can* just use `reinterpret_cast(itemData)`. That will, effecively, sort of use a C-style cast but it's far easier to track down `reinterpret_cast` in your code (should you need to) than to go looking for C-style casts. – Adrian Mole Oct 02 '21 at 17:29
  • @AdrianMole Thanks. My original code was: `auto* pEntry = static_cast(m_pComboDates->GetItemDataPtr(i));`. So I assumed I could continue to use `static_cast`. But your way works. Thanks. Ironic that the underlaying MFC code uses the C style casts still. Not surprised though. – Andrew Truckle Oct 02 '21 at 17:48
  • @AdrianMole You seen this: https://learn.microsoft.com/en-us/cpp/code-quality/c26490?view=msvc-160 ? It flags it and offers no alternative. – Andrew Truckle Oct 02 '21 at 18:09
  • @AdrianMole I used `#pragma warning(suppress: 26490)`. – Andrew Truckle Oct 02 '21 at 18:31
  • 1
    I'm just stunned and amazed that it warns about using `reinterpret_cast` but *not* about using C-style casts. In both your situation *and* in the example given in your linked document, you would *have* to use one or the other. – Adrian Mole Oct 02 '21 at 18:35
  • @AdrianMole It does actually flag c style casts and not using braces for arithmetic. A whole can of stuff! I didn’t want to bloat and detract from this question purpose which you answered. – Andrew Truckle Oct 02 '21 at 20:58
  • 1
    Is there a reason you don't go looking in MFC source code? All that MFC does is at last accessing Win32 APIs. If you look in MFC source code, you'll see everything you need to call with WIN32. – Joseph Willcoxson Oct 03 '21 at 16:38
  • @JosephWillcoxson I do understand that now. – Andrew Truckle Oct 03 '21 at 16:46

1 Answers1

3

I was wondering if there was any way to use native Win32 code here instead?

Yes, there is. The SendMessage function (and its returned value) is what you need …

Once you have the HWND of your combo-box, you can send it the CB_GETCOUNT message to ask it how many items it contains:

HWND hDatesCombo = ::GetDlgItem(GetParent()->GetSafeHwnd(), IDC_COMBOBOXEX_OCLM_WEEK_OF_MEETING);
LRESULT nItems = ::SendMessage(hDatesCombo, CB_GETCOUNT, 0, 0);

And, to get the item data associated with a particular entry, send the CB_GETITEMDATA message, with the (zero-based) index of the item in question as the wParam argument:

//...
LRESULT *ItemData = new LRESULT[static_cast<size_t>(nItems)];
for (int i = 0; i < nItems; ++i) {
    ItemData[i] = ::SendMessage(hDatesCombo, CB_GETITEMDATA, static_cast<WPARAM>(i), 0);
}
//...
delete[] ItemData; // When you're done with the data list

Of course, if your item data are pointers (such as if you have an owner-drawn combo with1 the CBS_HASSTRINGS style), you would need to modify the second code snippet accordingly, adding relevant the reinterpret_cast operations where necessary. (Note that both the LRESULT and WPARAM types are defined as being suitable for storing pointers.)


1 The linked M/S documentation page is a bit fuzzy on whether this applies to owner-drawn combos with or without the CBS_HASSTRINGS style.

Adrian Mole
  • 49,934
  • 160
  • 51
  • 83
  • Thank you. **But** does this work with a `CComboBoxEx` control? I tried your `CB_GETCOUNT` code as a started and it returns 0. So I am unsure these macros are compatible with `CComboBoxEx`. – Andrew Truckle Oct 02 '21 at 16:32
  • I saw a similar discussion here https://stackoverflow.com/q/51071401/2287576. – Andrew Truckle Oct 02 '21 at 16:33
  • Assuming you are right, how can we get to the `GetComboBoxCtrl` from where we are in the popup? – Andrew Truckle Oct 02 '21 at 16:36
  • `CComboBoxEx` uses `SetItem` etc.. but it **does** have the mthods I am using. – Andrew Truckle Oct 02 '21 at 16:38
  • Looking at the MFC header/source, I see that the `GetCount()` member for `CComboBoxEx()` is basically just this: `return (int)::SendMessage(m_hWnd, CB_GETCOUNT, 0, 0);`. (Plus an `IsWindow()` check.) So, can't see why my suggested code wouldn't work. [[ CComboBoxEx doesn't override the `GetCount()` member for its base `CComboBox`. ]] – Adrian Mole Oct 02 '21 at 16:41
  • It was because I was doing my tests in the popup window constructor and not `OnInitDialog`. – Andrew Truckle Oct 02 '21 at 16:45