使用 MCN_GETDAYSTATE 事件处理程序和动态 MONTHDAYSTATE 数组导致错误

Using MCN_GETDAYSTATE event handler and dynamic MONTHDAYSTATE arrays causing errors

这是我遇到的一个有趣的问题。我已经将 MCN_GETDAYSTATE 事件处理程序与我的 CMonthCalendarControl 一起使用了好几年,没有出现任何问题。处理程序如下所示:

void CHomeAwayMaintPage::OnGetDayStateCalendar(NMHDR* pNMHDR, LRESULT* pResult) 
{
    NMDAYSTATE      *pDayState = (NMDAYSTATE*)pNMHDR;
    MONTHDAYSTATE   mdState[3]; // last, this, next
    COleDateTime    datStart(pDayState->stStart);

    if (pDayState != nullptr)
    {
        InitDayStateArray(pDayState->cDayState, mdState, datStart);
        pDayState->prgDayState = mdState;
    }

    *pResult = 0;
}

它一直运行良好。 window 中的日历如下所示:

现在,在过去的两天里,我升级了这个 window 以支持调整大小,因此我设置了日历以调整大小。示例:

我像这样调整了事件处理程序(因为我们可能有任意数量的日历):

void CHomeAwayMaintPage::OnGetDayStateCalendar(NMHDR* pNMHDR, LRESULT* pResult) 
{
    NMDAYSTATE      *pDayState = (NMDAYSTATE*)pNMHDR;
    COleDateTime    datStart(pDayState->stStart);

    DWORD dwCount = MonthCal_GetMonthRange(m_Calender.GetSafeHwnd(), GMR_DAYSTATE, NULL);
    MONTHDAYSTATE *pmdState = new MONTHDAYSTATE[dwCount];

    if (pDayState != nullptr)
    {
        InitDayStateArray(pDayState->cDayState, pmdState, datStart);
        pDayState->prgDayState = pmdState;
    }

    delete[] pmdState;

    *pResult = 0;
}

然而,当我关闭 window 时,VS2017 出现了这个错误:

我试图注释掉 delete[] pmdState; 行,但没有任何区别。尝试显示 window:

时,我也经常收到此错误

如果我将日历控件的动态布局重置为仅移动控件而不调整它的大小(因此可以看到一个月)并将我的日状态事件处理程序恢复到以前的状态,这两个错误会消失离开它变得像以前一样稳定。

那么我做错了什么?

更新

这是InitDayStateArray方法。刚刚读取数据库:

void CHomeAwayMaintPage::InitDayStateArray(int iMonthCount,
     LPMONTHDAYSTATE pDayState, COleDateTime datStart)
{
    int                 iStartMonth, iLastMonth, iThisMonth, iMonth = 0;
    COleDateTime        datDay;
    COleDateTimeSpan    spnDay;
    CString             strDate;
    SCHEDULE_DATA_S     *psTalk = NULL;
    S_JOURNAL_ITEM      *psJournal = NULL; // AJT v11.2.0

    if (pDayState != NULL)
    {
        memset(pDayState, 0, sizeof(MONTHDAYSTATE)*iMonthCount);

        spnDay.SetDateTimeSpan(1,0,0,0);

        datDay = datStart;
        iStartMonth = datStart.GetMonth();
        iThisMonth = iStartMonth;
        iLastMonth = iThisMonth;
        do 
        {
            strDate.Format(_T("%d-%02d-%02d"),
                datDay.GetYear(),
                datDay.GetMonth(),
                datDay.GetDay());

            // try to get this entry from map
            psTalk = NULL;
            m_mapSPTalkDates.Lookup(strDate, (void*&)psTalk);
            if (psTalk != NULL && psTalk->uTalkNumber != 1000)
                BOLDDAY(pDayState[iMonth], datDay.GetDay());

            // AJT v11.2.0
            strDate = datDay.Format(_T("%Y-%m-%d"));
            psJournal = NULL;
            m_mapStrPtrJournalCalendar.Lookup(strDate, (void*&)psJournal);
            if (psJournal != NULL)
                BOLDDAY(pDayState[iMonth], datDay.GetDay());

            datDay = datDay + spnDay;
            iThisMonth = datDay.GetMonth();
            if (iThisMonth != iLastMonth)
            {
                iLastMonth = iThisMonth;
                iMonth++;
            }
        } while(iMonth < iMonthCount);
    }
}

DWORD dwCount = MonthCal_GetMonthRange(m_Calender.GetSafeHwnd(), GMR_DAYSTATE, NULL);

documentation表示第三个参数不能是NULL:

Pointer to a two-element array of SYSTEMTIME structures that will receive the lower and upper limits of the scope specified by dwFlag. The lower and upper limits are placed in lprgSysTimeArray[0] and lprgSysTimeArray[1], respectively. The time members of these structures will not be modified. This parameter must be a valid address and cannot be NULL.

当运行你的代码时,我得到dwCount = 4pDayState->cDayState = 395234

结果:

MONTHDAYSTATE *pmdState = new MONTHDAYSTATE[4];
...
InitDayStateArray(...)
{
    iMonthCount = pDayState->cDayState
    pDayState = pmdState;
    memset(pDayState, 0, sizeof(MONTHDAYSTATE)*395234);
    ...
}

请注意 memset 导致缓冲区溢出 (395234 - 4) * sizeof(MONTHDAYSTATE) 这会导致严重的问题。

您可以重写代码如下:

void CHomeAwayMaintPage::OnMcnSelchangeMonthcalendar1(NMHDR *pNMHDR, LRESULT *pResult)
{
    SYSTEMTIME systime[2];
    int month_count = MonthCal_GetMonthRange(m_Calender.GetSafeHwnd(),
            GMR_DAYSTATE, &systime);
    std::vector<MONTHDAYSTATE> vec(month_count); //or use new/delete

    COleDateTime date(systime[0]);
    COleDateTime end(systime[1]);
    COleDateTimeSpan spnDay;
    spnDay.SetDateTimeSpan(1, 0, 0, 0);
    while (date < end)
    {
        CString str = date.Format(_T("%Y-%m-%d"));
        TRACE(_T("datDay %s\n"), str.GetString());
        date = date + spnDay;
    }
    *pResult = 0;
}

更新

基于上述回答(谢谢)和此处评论中的那些我能够简化我的事件处理程序并正确执行:

void CHomeAwayMaintPage::OnGetDayStateCalendar(NMHDR* pNMHDR, LRESULT* pResult) 
{
    NMDAYSTATE *pDayState = (NMDAYSTATE*)pNMHDR;

    if (pDayState != nullptr)
        InitDayStateArray(pDayState->cDayState, 
            pDayState->prgDayState, COleDateTime(pDayState->stStart));

    *pResult = 0;
}

Here 它说:

"it receives the address of an array that provides this data."

我对 this 必须设置缓冲区的地方感到困惑。

如您所见,传入的结构已经分配了缓冲区。我只需要调整这些值。任何地方都没有内存分配。