找回密码
 立即注册

QQ登录

只需一步,快速开始

Carl

版主

49

主题

213

帖子

962

积分

版主

Rank: 7Rank: 7Rank: 7

积分
962

活字格认证微信认证勋章

QQ
Carl
版主   /  发表于:2009-12-16 10:40  /   查看:9469  /  回复:0
Post by "RoyLiu", 2009-04-28, 14:12
-----------------------------------------------------

项目中碰到一个问题,请各位大侠帮忙参详参详:

处理过程:

检索一定数目的数据放到form1的spread控件里,双击spread的其中一行,在双击的事件处理里打开另外一个form2以显示该条数据的详细信息。其中所有form继承一个基类form并实现一个初始化处理的接口来处理一些业务相关的form初始化,基类form重写OnShown方法,并在其中调用那个业务相关的初始化接口方法。另外,基类form还重写了WndProc方法,对一些指定的msg做特殊处理。

症状:

Vista系统里检索结果超过10000条数据时,双击spread的行可以打开form,执行了form的show方法,但是未激发OnShown的处理,以至于form没有正常初始化。但是当检索数据少于10000条时,可以正常打开form,也可以激发OnShown事件处理初始化form。而且同样的操作在XP系统下无论数据多少都正常。

调查:

我做了一些调查,发现在Vista系统里检索少量数据正常打开form的时候,根据tracelog记录,从form的Show方法到OnShown事件处理的过程中,发了一个ID为49502的消息,再往后就是OnShown里面的处理log,而XP在同样情况下发的消息的ID是49547,不知道和这个有没有关系。Vista里form不能正常打开时,tracelog到Show方法以后就结束了,没有后面的处理log。

另外,我做了一个试验,当在form2调用Show方法之前加一条弹出messagebox的语句,然后form2就能在vista下正常打开,原因不详。

以上就是所有情况。为什么在Vista系统里的一定条件下不能正常打开form2?还请各位不吝赐教。


Reply by "Leo", 2009-04-28, 14:35
-----------------------------------------------------

两点疑问:

1. Form的OnLoad是否被触发?

2. 当Form的OnLoad触发的时候,Form的IsHandleCreated状态是什么(在你说的Form的OnShow没有被调用的情况下)。


Reply by "KevinShan", 2009-04-28, 14:40
-----------------------------------------------------

只能根据你描述的现象瞎猜,给几点建议:

1,是否有哪里蹦异常了?特别注意Onload事件中是不是蹦异常了,因为在Onload之后调用OnShown请在IDE选项中监视所有异常。

2,为什么刚好是10000了?注意一下Handles个数是不是10000了?

3,可以封代码调试问题,比如先把base form中重载的WndProc代码注释掉,看还有没有问题等等。

4,Form此时如果IsHandleCreated属性为false,也不会调用OnShown


Reply by "RoyLiu",2009-04-28, 15:05
-----------------------------------------------------

并没有用OnLoad去做form的初始化,而是通过在OnShown里调用初始化form的接口,所以没有OnLoad方法。

过程中并没有异常。

10000并不是一个确切数据,根据数据不同,这个数字也不同,总体在10000左右。

注释掉WndProc的代码并不影响这个问题,还是会发生。

在调用完Show方法以后可以看到IsHandleCreated属性是true。

貌似一切都正常,同样的代码,只有在vista里数据超过10000左右时才无法激发OnShown,其他情况都可以正常激发,不知道为什么。

------------------------

调查发现,问题的原因是在我们封装的Spread控件里,双击的时候对Spread做了N次Focus(),N等于检索出来的数据条数。

所以当检索出10000条左右的数据时,双击其中一行打开新form之前,对Spread控件做了10000次Focus(),导致Form的OnShown没有激发。但是为什么做多次focus就导致激发不了form的onshown,这点暂时没有深究。

修改方法:在Focus外加上判断,如果已经focus,则不再做focus。

至此算是问题已解决。多谢各位的帮助。

--------------------

可能是Vista系统的bug,我做了一个小Demo试了一下,如果对form1上的任意控件做focus整10000次以后打开form2,则form2的OnShown不会被激发。

如果只focus9999次,OnShown就会被激发。


Reply by "Carl",2009-04-29, 14:04
-----------------------------------------------------

按照楼主的Demo在Vista下进行了调试,现在给大家报告一下结果:

1. Form.OnShown是在Form.OnLoad方法中通过Form.BeginInvoke调用到的。在10000次Focus之后,OnShown就调不到了。

2. 通过跟踪当前Application的Message,发现9999次Focus会导致9999个自定义事件(0xc03b),然后会收到一个0xc337。但是10000次Focus之后,最后的0xc337就消失了。如果这时再发一个0xc03b,会收到两个0xc337。证明超过10000之后,0xc337的消息处理就出了问题。

3. 怀疑是BeginInvoke的问题,所以在10000Focus之后,没有调用Form2.Show,而是通过BeginInvoke调用自己的一个方法,果然会失败。但是,如果在10000Focus之后,调用Application.DoEvents,则不会出错。

4. 通过反编译代码,得知BeginInvoke是通过PostMessage发送一个自定义事件(这里即0xc337,Control.threadCallbackMessage),然后再WndProc中处理这个Message来实现的。

5. 经查PostMessage的帮助,找到如下信息:

Windows 2000/XP: There is a limit of 10,000 posted messages per message queue. This limit should be sufficiently large. If your application exceeds the limit, it should be redesigned to avoid consuming so many system resources. To adjust this limit, modify the following registry key.

也就是说,因为Focus引发了10000次自定义事件(0xc03b),导致OS不会再立即处理下一个自定义事件(0xc337),使得BeginInvoke不能正常工作。
6. MSDN说这个值可以通过修改注册表来设置,最小为4000,最大为18000。路径如下:

  • HKEY_LOCAL_MACHINE
    • SOFTWARE
      • Microsoft
        • Windows NT
          • CurrentVersion
            • Windows
              • USERPostMessageLimit


7. 在Vista下修改这个值,重新启动电脑,发现Focus的数量确实受这个值影响。但是在XP的注册表中找不到这个键。手动添加这个键也没有影响。

8. 经查MS知识库,有一篇文章,微软承认这是XP的一个bug,说对应的HotFix已经有了。因此估计,XP在某个HotFix之后,已经修复了这个问题。怀疑Vista还没有发布对应的HotFix。
如有未尽事宜,请大家补充。

Reply by "RoyLiu",2009-04-29, 14:40
-----------------------------------------------------

XP的hotfix:http://support.microsoft.com/kb/327699
愿 Engine 归于沉寂,Timer 停止运动,Message Queue 不再流淌,Data Source 为我掌握

0 个回复

您需要登录后才可以回帖 登录 | 立即注册
返回顶部