开发app和做网站,用易语言做攻击网站软件下载,小程序买量平台,青海集团网站建设源码下载#xff1a;ActiveX-Clock-OCX 参照孙鑫的VC深入详解中第18章自定义ActiveX中的Clock例子(到18.3节之前)#xff0c;完成了OCX控件的制作#xff0c;而且也编译(Debug模式)、注册成功了#xff01;于是又创建了一个MFC基于对话框的测试程序#x…源码下载ActiveX-Clock-OCX 参照孙鑫的VC深入详解中第18章自定义ActiveX中的Clock例子(到18.3节之前)完成了OCX控件的制作而且也编译(Debug模式)、注册成功了于是又创建了一个MFC基于对话框的测试程序在对话框中放入了这个Clock控件界面如下 接下来右击Clock控件选择“属性”切换到“设置时间间隔面板”更改时间后切换到其他一个属性页这时就出现assert宏异常了看图 真是奇怪孙鑫的教程里也没提到有这个问题。后来试了下在Release模式下编译成功的OCX发现在切换属性页时却没有这样的问题一切都是正常的。
看来是优化的问题吧详情未知猜测而已 看到第18章的总结(Page708)才发现原来孙鑫老师还是提到了这个问题的。
他说出现这种错误的原因是当将Clock控件放到VB的Form上时该控件的窗口已经创建也就是说CClockCtrl类的OnCreate()方法被执行了这样就设置了定时器。而在VC的对话框上插入Clock控件时却没有调用CClockCtrl类的OnCreate()方法当修改Interval属性时会调用CClockCtrl类的OnIntervalChanged()方法在这个方法中调用了KillTimer(1)因为定时器根本没有创建因此就出现了非法操作。解决办法是用一个变量保存定时器的返回值然后在OnIntervalChanged()方法中对返回值进行判断。 于是我将代码改成下面的样子
void CClockCtrl::OnIntervalChanged()
{
if(m_nInterval 0 || m_nInterval 6000)
m_nInterval 1000;
else
m_nInterval m_nInterval / 1000 * 1000;
//if(timer_flag ! 0)
{
MessageBox(OnIntervalChanged: going to do KillTimer());
//KillTimer(1);
timer_flag 0;
}
MessageBox(OnIntervalChanged: going to do SetTimer());
//timer_flag SetTimer(1, m_nInterval, NULL);
SetTimer(1, m_nInterval, NULL);
char message[100] {0};
sprintf(message, timer_flag %d, timer_flag);
MessageBox(message);
SetModifiedFlag();
} 再经过测试发现在切换属性页时弹出窗口输出了“OnIntervalChanged: going to do SetTimer()”后就出现了ASSERT()宏异常可见这个异常是出现在SetTimer()内部的。我们都知道ASSERT()宏只有在Debug模式下才会起作用在Release下是不会起作用的这就是为什么使用Release时生成的ocx时不会弹出ASSERT()宏异常窗口的原因了。可是为什么SetTimer()会失败呢
先来看下Plateform SDK中的SetTimer()原型吧
UINT_PTR SetTimer(
HWND hWnd, // handle to window
UINT_PTR nIDEvent, // timer identifier
UINT uElapse, // time-out value
TIMERPROC lpTimerFunc // timer procedure
);
看到第一个参数hWnd了吗这是与窗口的句柄相关联的但是孙鑫老师也说了“将Clock控件放到VB的Form上时该控件的窗口已经创建”但是“在VC的对话框上插入Clock控件时却没有调用CClockCtrl类的OnCreate()方法 ”这里的关键不是指“OnCreate()中的SetTimer()”而是指“窗口没有创建”所以“窗口对应的句柄又将是多少”呢正是因为控件窗口没有创建所以“CClockCtrl::OnIntervalChanged() ”中的“SetTimer()”和“KillTimer()”都将会失败而且失败的主要原因是在其函数内部对“窗口句柄”的ASSERT()判断。因此我认为孙鑫老师说的“解决办法”是行不通的除非不调用KillTimer()和SetTimer()但是这样的话就达不到控制多少秒触发一次OnDraw()的效果了
经过调试终于在“D:\Program Files\Microsoft Visual Studio\VC98\MFC\Include\AFXWIN2.INL文件中的第166-171行”找到了SetTimer()和KillTimer()的具体实现
_AFXWIN_INLINE UINT CWnd::SetTimer(UINT nIDEvent, UINT nElapse,
void (CALLBACK* lpfnTimer)(HWND, UINT, UINT, DWORD))
{ ASSERT(::IsWindow(m_hWnd)); return ::SetTimer(m_hWnd, nIDEvent, nElapse,
(TIMERPROC)lpfnTimer); }
_AFXWIN_INLINE BOOL CWnd::KillTimer(int nIDEvent)
{ ASSERT(::IsWindow(m_hWnd)); return ::KillTimer(m_hWnd, nIDEvent); } 因此我认为解决的办法有3种
1. 使用“Release方式生成的OCX”
2. 越过KillTimer()和SetTimer()中“ASSERT(::IsWindow(m_hWnd)); ”即将CClockCtrl::OnIntervalChanged() 中的内容修改如下
void CClockCtrl::OnIntervalChanged()
{
if(m_nInterval 0 || m_nInterval 6000)
m_nInterval 1000;
else
m_nInterval m_nInterval / 1000 * 1000;
//KillTimer(1); // 为了越过KillTimer()中的ASSERT(::IsWindow(m_hWnd));
::KillTimer(m_hWnd, 1);
::SetTimer(m_hWnd, 1, m_nInterval, NULL);
//SetTimer(1, m_nInterval, NULL);
SetModifiedFlag();
} 这时在切换属性页时虽然也会执行::KillTimer和::SetTimer()而且其中的m_hWnd可能为一个非法的值但是起码不会弹出ASSERT()宏异常窗口大不了就是这两个函数调用失败而已所以也解决了这个问题。
3. 判断控件当前状态是否为运行状态如果是才调用SetTimer()和KillTimer()即修改CClockCtrl::OnIntervalChanged()的内容如下 void CClockCtrl::OnIntervalChanged()
{
if(m_nInterval 0 || m_nInterval 6000)
m_nInterval 1000;
else
m_nInterval m_nInterval / 1000 * 1000;
if(AmbientUserMode())
{
KillTimer(1); // 为了越过KillTimer()中的ASSERT(::IsWindow(m_hWnd));
//::KillTimer(m_hWnd, 1);
//::SetTimer(m_hWnd, 1, m_nInterval, NULL);
SetTimer(1, m_nInterval, NULL);
}
SetModifiedFlag();
} 如果各位有什么不同的看法欢迎提出来探讨