热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

自定义ActiveX组件在设计阶段,切换属性页后出现异常

源码下载:ActiveX-Clock-OCX参照孙鑫的中第18章自定义ActiveX中的Clock例子(到18.3节之前),完成了O

源码下载:ActiveX-Clock-OCX

 

参照孙鑫的<>中第18章自定义ActiveX中的Clock例子(到18.3节之前)&#xff0c;完成了OCX控件的制作&#xff0c;而且也编译(Debug模式)、注册成功了&#xff01;于是又创建了一个MFC基于对话框的测试程序&#xff0c;在对话框中放入了这个Clock控件&#xff0c;界面如下&#xff1a;

接下来右击Clock控件&#xff0c;选择“属性”&#xff0c;切换到“设置时间间隔面板”&#xff0c;更改时间后&#xff0c;切换到其他一个属性页&#xff0c;这时就出现assert宏异常了&#xff0c;看图&#xff1a;

 

 

 

真是奇怪&#xff0c;孙鑫的教程里也没提到有这个问题。后来试了下在Release模式下编译成功的OCX&#xff0c;发现在切换属性页时却没有这样的问题&#xff0c;一切都是正常的。

看来是优化的问题吧&#xff0c;详情未知&#xff0c;猜测而已&#xff01;

 

看到第18章的总结(Page708)才发现&#xff0c;原来孙鑫老师还是提到了这个问题的。

他说出现这种错误的原因是&#xff1a;当将Clock控件放到VB的Form上时&#xff0c;该控件的窗口已经创建&#xff0c;也就是说&#xff0c;CClockCtrl类的OnCreate()方法被执行了&#xff0c;这样就设置了定时器。而在VC的对话框上插入Clock控件时&#xff0c;却没有调用CClockCtrl类的OnCreate()方法&#xff0c;当修改Interval属性时&#xff0c;会调用CClockCtrl类的OnIntervalChanged()方法&#xff0c;在这个方法中&#xff0c;调用了KillTimer(1)&#xff0c;因为定时器根本没有创建&#xff0c;因此就出现了非法操作。解决办法是&#xff1a;用一个变量保存定时器的返回值&#xff0c;然后在OnIntervalChanged()方法中对返回值进行判断。

 于是我将代码改成下面的样子&#xff1a;

void CClockCtrl::OnIntervalChanged()
{
if(m_nInterval <0 || m_nInterval > 6000)
m_nInterval &#61; 1000;
else
m_nInterval &#61; m_nInterval / 1000 * 1000;
//if(timer_flag !&#61; 0)
{
MessageBox("OnIntervalChanged: going to do KillTimer()");
//KillTimer(1);
timer_flag &#61; 0;
}
MessageBox("OnIntervalChanged: going to do SetTimer()");
//timer_flag &#61; SetTimer(1, m_nInterval, NULL);
SetTimer(1, m_nInterval, NULL);
char message[100] &#61; {0};
sprintf(message, "timer_flag &#61; %d", timer_flag);
MessageBox(message);
SetModifiedFlag();
}


再经过测试&#xff0c;发现在切换属性页时&#xff0c;弹出窗口输出了“OnIntervalChanged: going to do SetTimer()”后就出现了ASSERT()宏异常&#xff0c;可见这个异常是出现在SetTimer()内部的。我们都知道&#xff0c;ASSERT()宏只有在Debug模式下才会起作用&#xff0c;在Release下是不会起作用的&#xff0c;这就是为什么使用Release时生成的ocx时不会弹出ASSERT()宏异常窗口的原因了。可是为什么SetTimer()会失败呢&#xff1f;&#xff1f;&#xff1f;

先来看下Plateform SDK中的SetTimer()原型吧&#xff1a;

UINT_PTR SetTimer(
HWND hWnd, // handle to window
UINT_PTR nIDEvent, // timer identifier
UINT uElapse, // time-out value
TIMERPROC lpTimerFunc // timer procedure
);


看到第一个参数hWnd了吗&#xff0c;这是与窗口的句柄相关联的&#xff0c;但是孙鑫老师也说了“将Clock控件放到VB的Form上时&#xff0c;该控件的窗口已经创建”&#xff0c;但是“在VC的对话框上插入Clock控件时&#xff0c;却没有调用CClockCtrl类的OnCreate()方法 ”&#xff0c;这里的关键不是指“OnCreate()中的SetTimer()”&#xff0c;而是指“窗口没有创建”&#xff0c;所以“窗口对应的句柄又将是多少”呢&#xff1f;正是因为控件窗口没有创建&#xff0c;所以“CClockCtrl::OnIntervalChanged() ”中的“SetTimer()”和“KillTimer()”都将会失败&#xff0c;而且失败的主要原因是在其函数内部对“窗口句柄”的ASSERT()判断。因此&#xff0c;我认为孙鑫老师说的“解决办法”是行不通的&#xff0c;除非不调用KillTimer()和SetTimer()&#xff0c;但是这样的话&#xff0c;就达不到控制多少秒触发一次OnDraw()的效果了&#xff01;

经过调试&#xff0c;终于在“D:\Program Files\Microsoft Visual Studio\VC98\MFC\Include\AFXWIN2.INL文件中的第166-171行”找到了SetTimer()和KillTimer()的具体实现&#xff1a;

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


因此&#xff0c;我认为解决的办法有3种&#xff1a;

1. 使用“Release方式生成的OCX”

2. 越过KillTimer()和SetTimer()中“ASSERT(::IsWindow(m_hWnd)); ”&#xff0c;即将CClockCtrl::OnIntervalChanged() 中的内容修改如下&#xff1a;

void CClockCtrl::OnIntervalChanged()
{
if(m_nInterval <0 || m_nInterval > 6000)
m_nInterval &#61; 1000;
else
m_nInterval &#61; 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();
}


这时&#xff0c;在切换属性页时虽然也会执行::KillTimer和::SetTimer()&#xff0c;而且其中的m_hWnd可能为一个非法的值&#xff0c;但是起码不会弹出ASSERT()宏异常窗口&#xff0c;大不了就是这两个函数调用失败而已&#xff0c;所以也解决了这个问题。

3. 判断控件当前状态是否为运行状态&#xff0c;如果是才调用SetTimer()和KillTimer()&#xff0c;即修改CClockCtrl::OnIntervalChanged()的内容如下&#xff1a;

 

void CClockCtrl::OnIntervalChanged()
{
if(m_nInterval <0 || m_nInterval > 6000)
m_nInterval &#61; 1000;
else
m_nInterval &#61; 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();
}


如果各位有什么不同的看法&#xff0c;欢迎提出来探讨&#xff01;


推荐阅读
author-avatar
苦蔷薇1988
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有