代码之家  ›  专栏  ›  技术社区  ›  Andrew Truckle

使用mcn_getdaystate事件处理程序和导致错误的动态monthdaystate数组

  •  0
  • Andrew Truckle  · 技术社区  · 6 年前

    这是我遇到的一个有趣的问题。我用过 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;
    }
    

    它一直工作得很好。窗口中的日历如下所示:

    Calendar

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

    MultipleCalendar

    我调整了事件处理程序(因为我们可能有任意数量的日历),如下所示:

    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;
    }
    

    然而,当我关闭窗口时,VS2017会引发以下错误:

    Error 1

    我试着评论 delete[] pmdState; 线和那没什么区别。在尝试显示窗口时,我也经常会遇到此错误:

    Error 2

    如果我将日历控件的动态布局重置为只移动控件而不调整其大小(因此一个月可见),并将我的日状态事件处理程序还原为以前的状态,则这两个错误会消失,并像以前一样变得稳定。

    我在这里做错什么了?

    更新

    这就是 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);
        }
    }
    
    1 回复  |  直到 6 年前
        1
  •  3
  •   Andrew Truckle    6 年前

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

    这个 documentation 说第三个参数不能 NULL :

    指向双元素数组的指针 SYSTEMTIME 将接收由dwflag指定的作用域的上下限的结构。下限和上限位于 lprgSysTimeArray[0] lprgSysTimeArray[1] ,分别是。这些结构的时间成员将不会被修改。此参数必须是有效的地址,不能 无效的 .

    运行代码时,我得到 dwCount = 4 pDayState->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 它说:

    “它接收提供此数据的数组的地址。”

    我被搞糊涂了 this 你必须设置缓冲区的地方。

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