自定义ActiveX组件在设计阶段,切换属性页后出现异常
源码下载: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();
}
如果各位有什么不同的看法,欢迎提出来探讨!
自定义ActiveX组件在设计阶段,切换属性页后出现异常相关推荐
- vue2自定义分页组件,可设置每页显示数量,指定跳转具体页面
https://blog.csdn.net/yangwei282367751/article/details/82722840 分页组件 <template> <div> ...
- MFC编程入门之十三(对话框:属性页对话框及相关类的介绍)
前面讲了模态对话框和非模态对话框,本节来将一种特殊的对话框--属性页对话框. 属性页对话框的分类 属性页对话框想必大家并不陌生,XP系统中桌面右键点属性,弹出的就是属性页对话框,它通过标签切换各个页面 ...
- 鸡啄米vc++2010系列12(属性页对话框)
前面讲了模态对话框和非模态对话框,本节开始鸡啄米讲一种特殊的对话框--属性页对话框.另外,本套教程所讲大部分对VC++各个版本均可适用或者稍作修改即可,但考虑到终究还是基于VS2010版本的,所以将& ...
- 对话框属性页(VC_MFC)
目录 属性页编写总体思路 CPropertySheet 编程 Tab Control 控件 (本章节中例子都是用 VS2005 编译调试的) 属性页编写总体思路 大体思想: 设置对话框属性页属性: 在 ...
- WX小程序组件不支持hidden属性(uni-app v-show直接作用在自定义的在组件上不起作用)
文章目录 问题描述 过程分析 问题突破口 结论 代码验证 问题描述 今天在工作中,同事使用uni-app开发,想利用v-show指令实现页面内容切换,结果在微信小程序中,页面的内容全部显示出来了. 看 ...
- CPropertyPage::OnSetActive()和OnKillActive()函数:属性页切换时的处理函数
CPropertyPage::OnKillActive virtual BOOL OnKillActive(); 返回值: 如果数据被成功更新则返回非零值:否则返回0. 说明: 当页不再是活动页时,框 ...
- dplayer js控制 自动全屏_vue-video-player 通过自定义按钮组件实现全屏切换效果【推荐】...
最近公司的产品上线,一些高级功能在基础版本中不对用户开发,通过视频的形式展示. 产品开发用的是 vue, 经同事介绍使用了vue-video-player视频播放插件,通过 demo案例很快实现了视频 ...
- Angular 自定义分页组件,自定义每页显示个数
后端返回列表是全部数据,分页前端做的,自定义分页组件实现前端分页,下图为分页效果: 1.创建page.component.html <nav class="navigation&quo ...
- Unity自定义UI组件(十一) 雷达图、属性图
前言 借用梦想世界宠物属性图 想必大家都在游戏中见过属性图用于展示多种属性的数值,可以较为直观的对比某种属性的缺陷或者是哪种属性有优势.在三维可视化领域也会遇到类似的属性对比,用属性图来展示最为合适. ...
最新文章
- Visual Studio 2019没有Setup安装项目(Microsoft Visual Studio Installer Projects)的官方解决方案
- linux文件分别打包命令,Linux文件打包命令
- Linux 修改密码root(账号)
- 数组的几种定义方式及初始化
- 【转载】关于.NET下开源及商业图像处理(PSD)组件
- 生成树切分matlab_机器学习——手把手教你用Python实现回归树模型
- PCM(Pulse-code modulation)脉冲编码调制
- Python3的迭代器
- 《基于LSTM长短时神经网络的电机旋转振动单步预测》
- vue3.0 前进刷新后退缓存
- 千锋教育python老师_千锋老师分享Python经典面试题
- 彻底解决2440触摸屏跳点以及抖动问题
- 模拟电路仿真LTspice(3):三极管共发射极放大电路
- Python笔记day04(基础)|列表、元组
- Macbook Pro 外接显卡实现Tensorflow GPU运行之内屏输出
- 不经意传输协议-密码学
- Ureal:用ue4做出游戏中的爆炸特效真实感和力量感
- android onCreate与onCreateView的区别
- 【Electron】桌面应用开发
- 【zephyr】apds9660 接近(Proximity)传感器 驱动模型实现方式(一)
热门文章
- Oracle Row cache lock图解
- 够学习一辈子的生活经典
- 在应用程序级别以外使用注册为 allowDefinition='MachineToApplication' 的节是错误
- 吹气球问题的C语言编程,C语言怎样给一个数组中的数从大到小排序
- leetcode945. 使数组唯一的最小增量(排序)
- css注释_CSS注释示例–如何注释CSS
- linux gcc 示例_最好的Linux示例
- css 计算属性的应用_如何使用一点CSS Grid魔术设计计算器应用
- draft.js_如何使用快捷方式在Draft.js中创建有序列表和无序列表
- 如何使用JavaScript中的工厂函数构建可靠的对象