1

I have a registered user message. This is the handler:

afx_msg LRESULT CChristianLifeMinistryEditorDlg::OnGetDate(WPARAM wParam, LPARAM lParam)
{
    const auto eDateType = gsl::narrow<CInsertDateDlg::EnumDateType>(wParam);

    if (eDateType == CInsertDateDlg::EnumDateType::Start)
    {
        return CInPlaceDT::GetLongDate(m_datStartDate);
    }

    if (eDateType == CInsertDateDlg::EnumDateType::End)
    {
        return CInPlaceDT::GetLongDate(m_datEndDate);
    }

    if (eDateType == CInsertDateDlg::EnumDateType::Meeting)
    {
        return CInPlaceDT::GetLongDate(m_pEntry->GetMeetingDate());
    }

    return CInPlaceDT::GetLongDate(COleDateTime::GetCurrentTime());
}

The referenced function is:

long CInPlaceDT::GetLongDate(COleDateTime timDate) noexcept
{
    const long lDate = (timDate.GetYear() * 10000) +
                    (timDate.GetMonth() * 100 ) +
                     timDate.GetDay();

    return lDate;
}

I use the code like this (some lines stripped out for simplicity):

// 1. CEditTextDlg
// 2. CSMCustomizeDlg
// 3. CChristianLifeMinistryEditor
const CWnd* pParent = GetParent()->GetParent()->GetParent();
if (pParent != nullptr)
{
    COleDateTime datToUse;
    long lDate{};

    lDate = gsl::narrow<long>(pParent->SendMessage(theApp.UWM_GET_DATE_MSG,
        gsl::narrow<WPARAM>(CInsertDateDlg::EnumDateType::Start)));
    CInPlaceDT::GetOleDateTime(lDate, datToUse);

    lDate = gsl::narrow<long>(pParent->SendMessage(theApp.UWM_GET_DATE_MSG,
        gsl::narrow<WPARAM>(CInsertDateDlg::EnumDateType::End)));
    CInPlaceDT::GetOleDateTime(lDate, datToUse);

    lDate = gsl::narrow<long>(pParent->SendMessage(theApp.UWM_GET_DATE_MSG,
        gsl::narrow<WPARAM>(CInsertDateDlg::EnumDateType::Meeting)));
    CInPlaceDT::GetOleDateTime(lDate, datToUse);
}

The second referenced function is:

void CInPlaceDT::GetOleDateTime(long lDate, COleDateTime &timDate) noexcept
{
    const auto nYear = lDate / 10000;
    const auto nMonth = (lDate / 100) % 100;
    const auto nDay = lDate % 100;

    timDate.SetDateTime(nYear, nMonth, nDay, 0, 0, 0);
}

This concept works. It converts the date to a long, returns it via the message and is converted back to a date object.

I just wondered if I could pass the object as a date variable? This is all within the same process. A grandchild dialog is getting a date from the grandparent dialog.


Update

I tried to follow the suggestion in the comments:

afx_msg LRESULT CChristianLifeMinistryEditorDlg::OnGetDate(WPARAM wParam, LPARAM lParam)
{
    const auto eDateType = gsl::narrow<CInsertDateDlg::EnumDateType>(wParam);

    if (eDateType == CInsertDateDlg::EnumDateType::Start)
    {
        return gsl::narrow<LRESULT>(&m_datStartDate); // CInPlaceDT::GetLongDate(m_datStartDate);
    }

    if (eDateType == CInsertDateDlg::EnumDateType::End)
    {
        return gsl::narrow<LRESULT>(&m_datEndDate); // CInPlaceDT::GetLongDate(m_datEndDate);
    }

    if (eDateType == CInsertDateDlg::EnumDateType::Meeting)
    {
        return gsl::narrow<LRESULT>(&(m_pEntry->GetMeetingDate())); // CInPlaceDT::GetLongDate(m_pEntry->GetMeetingDate());
    }

    return 0;
    // return CInPlaceDT::GetLongDate(COleDateTime::GetCurrentTime());
}

But it does not like:

return gsl::narrow<LRESULT>(&(m_pEntry->GetMeetingDate())); 

It says:

error C2102: '&' requires l-value

Andrew Truckle
  • 17,769
  • 16
  • 66
  • 164
  • 1
    Since you are using `SendMessage`, can't you just pass the address of the `COleDateTime` object in the `LPARAM`? – Jim Rhodes Jan 29 '23 at 17:10
  • @JimRhodes So cast the address of the date as a `LRESULT` and then cast the `LRESULT` back as a `COleDateTime *` pointer. Right? Only problem ius that fallback return clause that uses `GetCurrentTime`. – Andrew Truckle Jan 29 '23 at 17:27
  • Cast it to an `LPARAM`. – Jim Rhodes Jan 29 '23 at 17:42
  • @JimRhodes But tge Send function returns a LRESULT. – Andrew Truckle Jan 29 '23 at 17:46
  • Sorry, I didn't I really read your comment. Yes, for send use `LPARAM` cast for result use `LRESULT` cast. For the `GetCurrentTime` case, I guess you could add another member variable or return NULL to use current time. – Jim Rhodes Jan 29 '23 at 17:56
  • 1
    One problem is that you try to return different kinds of objects using the same interface: `m_datStartDate` & `m_datEndDate` are data members, `GetMeetingDate()` a function result (not an l-value, so no & of it) and `GetCurrentTime()` a newly constructed object. The `SendMessage()` function though can only return directly items that can be fitted into a `LRESULT`/`LPARAM`-sized variable... – Constantine Georgiou Jan 29 '23 at 19:43
  • 1
    ... Therefore I think there are two possible solutions:.. A. As you initially did, convert the `COleDateTime` object to the custom `long` date format (or a more standard one like `time_t`) and upon returning convert it to the desired type. B. Have the dialog creating a `new` `COleDateTime` object (copy-constructed from the data members, function call or constructor) and return its address via the `LRESULT` or `LPARAM` of the message - on return the caller owns the object and should release it (`delete`) when its no longer needed. – Constantine Georgiou Jan 29 '23 at 19:44
  • A `COleDateTime` ultimately just wraps a `DATE` value (which is a type alias for a `double` value). As such, you can simply pass it by value. For a 64-bit build this fits into either one of `WPARAM` or `LPARAM`; for a 32-bit build you'd have to split it in halves. The receiver can either use the `DATE` as-is, or convert it back into a `COleDateTime` by using the c'tor that takes a `DATE` value. – IInspectable Jan 30 '23 at 08:40
  • Just realized that it's about *returning* the value, not passing it along. Most of the above still applies with one exception: While a `double` fits inside an `LRESULT` for a 64-bit build, it won't for a 32-bit build. For a 64-bit build you can simple return the `DATE` (i.e. `double`), whereas for a 32-bit build you'd have to invent a different solution (e.g. returning a pointer to some data, making sure the sender or receiver know who is responsible for cleanup, and when that must happen). – IInspectable Jan 30 '23 at 08:48

1 Answers1

3

As other window function do it too. Pass a pointer in LPARAM. Save the value in that pointer

afx_msg LRESULT CChristianLifeMinistryEditorDlg::OnGetDate(WPARAM , LPARAM lParam)
{
   *reinterpret_cast<COleDateTime*>(lParam) = myValue;

Remember that a COleDateTime is just a DATE (is a double). You may use a VARIANT* to that may handle all types of data (even an empty/null COleDateTime)

In detail: Use VT_NULL or VT_EMPTY for COleDateTime::null. For COleDateTime::error/invalid you can use VT_ERROR with the value E_INVALIDARG or any suitable.

For a COleDateTime::valid just store the m_dt member in the VARIANT with VT_DATE.

xMRi
  • 14,982
  • 3
  • 26
  • 59
  • 1
    The [`m_status`](https://learn.microsoft.com/en-us/cpp/atl-mfc-shared/reference/coledatetime-class#m_status) member that designates the status of the current `DATE` value is stored separately. A `VARIANT` can only store a `DATE` value. As such it cannot represent the additional status property. – IInspectable Jan 30 '23 at 09:20
  • But you can easily transfer the COleDateTime status into a VARIANT NULL/Empty. And even the Error status can be transferred in a corresponding HRESULT. I am doing this all the time in my COM Interfaces. – xMRi Jan 30 '23 at 09:56
  • Unless you know of a way to encode arbitrary information in zero bits, you cannot convey information worth *n + 1* bits in *n* bits. Having a hard time following your rationale here... – IInspectable Jan 30 '23 at 12:24
  • *"you can easily transfer the COleDateTime status into a VARIANT NULL/Empty"* - I'm not aware of a way to encode this information in a [`VARIANT`](https://learn.microsoft.com/en-us/windows/win32/api/oaidl/ns-oaidl-variant). Unless you explain what *"I am doing this all the time"* translate to code, this will be a -1. – IInspectable Jan 30 '23 at 16:16
  • Added this to my answer. – xMRi Jan 31 '23 at 10:32
  • Thanks, but using variants is only complicating the just as much as using a conversion to long. Passing the address to a date pointer and filling it was the way to go. Job done in half the code and easier to read. – Andrew Truckle Jan 31 '23 at 13:04
  • Sure, that's one way to do it. But now you have to manage object lifetimes, and the compiler isn't going to help you in making sure that the pointer is still valid by the time you're using it. Passing values, on the other hand, doesn't come with the obligation of managing lifetimes. – IInspectable Jan 31 '23 at 13:41
  • On the other hand think about the usage of COleDateTime. Depending how granular you need the date, you can simply work with a DWORD just representing the minutes of COleDateTime *1440... depends to if you need the state field in the COleDateTime. – xMRi Jan 31 '23 at 13:57