使用 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 = 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 它说:
"it receives the address of an array that provides this data."
我对 this 必须设置缓冲区的地方感到困惑。
如您所见,传入的结构已经分配了缓冲区。我只需要调整这些值。任何地方都没有内存分配。
这是我遇到的一个有趣的问题。我已经将 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 inlprgSysTimeArray[0]
andlprgSysTimeArray[1]
, respectively. The time members of these structures will not be modified. This parameter must be a valid address and cannot beNULL
.
当运行你的代码时,我得到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 它说:
"it receives the address of an array that provides this data."
我对 this 必须设置缓冲区的地方感到困惑。
如您所见,传入的结构已经分配了缓冲区。我只需要调整这些值。任何地方都没有内存分配。