现象:这个错误的触发是在关闭时触发的,消息循环退出时,正常来讲对软件体验没有任何影响,因为感知不到,几乎不存在
发现经过:自己在公司写了几个小工具,几个同事在用,做了个网络收集错误信息的功能,每次触发错误都会提交到服务器,然后就发现数据库存在同一个错误,并且是每次关闭软件几乎都会触发,因为不影响使用,所以一直没管,最近在处理数据时发现这些错误太影响数据筛选了,就着手分析了一下。
先说结论:问题不是很复杂,但是不太容易发现
tab.aardio 中在组件销毁时会发送【removeAll函数中】,下面是代码,是通过for循环进行删除的
removeAll = function(){
for(i=#owner._forms;1;-1){
::SendMessage(owner._forms[i][["hwnd"]],0x10/*_WM_CLOSE*/);
..table.remove(owner._forms,i );
}
owner.form = null;
..table.pop(owner._history,#owner._history)
::SendMessage(owner.hwnd ,4873 )
}
但是如何tab窗口在激活,在前台时,在win.ui._aardio中,【第979行】,也会有一个销毁窗口的循环,
for(k,f in _forms){
::SendMessage(f.hwnd,0x2/*_WM_DESTROY*/);
}
消息在退出循环时,如何ui的删除恰好在rab的removeAll进入循环后,就可能导致tab中for循环的句柄被提前销毁,就会导致tab中的removeAll报错。
改进方法:在removeAll函数的for循环内SendMessage前做一次句柄是否为空的判断即可
附件是一个演示工程,内容很简单,都是空的。
1、首先贴一下报错信息
{File}:E:\Aardio_IDE\lib\win\ui\ctrl\tab.aardio
{Line}:#163
{Error}:
{Calling}:'SendMessage'
{Bad argument}:@1
'
{Expected}:number
{Got}:null'
调用栈
[kernel]: in function 'SendMessage'
E:\Aardio_IDE\lib\win\ui\ctrl\tab.aardio:163: in function 'removeAll'
E:\Aardio_IDE\lib\win\ui\ctrl\tab.aardio:205: in function '_onDestroy'
E:\Aardio_IDE\lib\win\ui\_.aardio:898: in function 'proc'
E:\Aardio_IDE\lib\win\ui\_.aardio:305: in function <E:\Aardio_IDE\lib\win\ui\_.aardio:297>
[kernel]: in function 'SendMessage'
E:\Aardio_IDE\lib\win\ui\_.aardio:979: in function 'proc'
E:\Aardio_IDE\lib\win\ui\_.aardio:305: in function <E:\Aardio_IDE\lib\win\ui\_.aardio:297>
[kernel]: in function '_defWindowProc'
...
..._IDE\lib\win\ui\ctrl\metaProperty\_.aardio:236: in function 'close'
...\Aardio_IDE\lib\win\ui\simpleWindow.aardio:25: in function 'oncommand'
E:\Aardio_IDE\lib\win\ui\tracker.aardio:87: in function <E:\Aardio_IDE\lib\win\ui\tracker.aardio:67>
[kernel]: in function 'call'
..._IDE\lib\win\ui\ctrl\metaProperty\_.aardio:304: in function 'wndproc__'
..._IDE\lib\win\ui\ctrl\metaProperty\_.aardio:339: in function <..._IDE\lib\win\ui\ctrl\metaProperty\_.aardio:336>
[kernel]: in function 'messageTranslateDispatch'
E:\Aardio_IDE\lib\win\_.aardio:557: in function 'parseMessage'
E:\Aardio_IDE\lib\win\_.aardio:593: in function <E:\Aardio_IDE\lib\win\_.aardio:581>
(tail call): ?
2、可以看到是tab文件的163行报错
removeAll = function(){
for(i=#owner._forms;1;-1){
/*这一行*/
..table.remove(owner._forms,i );
}
owner.form = null;
..table.pop(owner._history,#owner._history)
::SendMessage(owner.hwnd ,4873 )
}
错误信息看出是参数1句柄为空。
3、我们修改库文件,输出owner._forms到文件中看看,这里不能用console打印,因为是在关闭时触发
removeAll = function(){
for(i=#owner._forms;1;-1){
::SendMessage(owner._forms[i][["hwnd"]],0x10/*_WM_CLOSE*/);
..table.remove(owner._forms,i );
}
owner.form = null;
..table.pop(owner._history,#owner._history)
::SendMessage(owner.hwnd ,4873 )
}
结果显示数据如下:
{
[1]={hwnd = 658878;};
[2]={hwnd = 396666;};
[3]={hwnd = 1183420;}
}
也就意味着在函数进入时
owner._forms[i][["hwnd"]]
是不会出现空值的,也说明在循环过程中,某一个环节,传入进去的hwnd为空了。
于是我们进一步打印信息:
多次运行触发错误,取其中一次数据如下:
发现循环在循环第二次时,owner._forms整个表都为空了。
继续寻找,发现win.ui._aardio也在销毁同一个句柄,其代码如下,我们也做一下信息打印
打印信息如下:
图中,首先在tab的循环外有2个句柄待关闭,注意第一个句柄,这个句柄在UI的for循环中,直接被销毁了,图中绿色和黄色的句柄,导致循环第二次进入时已经没有句柄了。
所以tab.aardio文件就会报错了。