注册 登录
编程论坛 VFP论坛

求助:服务器异常报错,如何终止报错进程

laowan001 发布于 2022-01-25 17:36, 5635 次点击
后台是阿里云服务器,有个采集数据的应用程序,每隔几分钟执行一次,程序中使用了on error,如果程序运行过程中出现错误,会自行中止,但最近出现了系统错误(这种错误on erron无法捕捉到),弹出了报错窗口,一直就会停在这个窗口,致使后台程序无法继续运行。
问题:有什么办法中止这个报错窗口进程?

截图1:报错窗口
只有本站会员才能查看附件,请 登录

截图2:报错窗口-续
只有本站会员才能查看附件,请 登录

截图3:系统配置
只有本站会员才能查看附件,请 登录


显示是urlmon.dll出错,这是个系统文件,不知是何原因产生的错误,请高人指点
如果能避免这种错误,也能解决问题。如果出现这种错误,能终止这个报错窗口,使后台程序能再次运行也行

48 回复
#2
nbwww2022-01-25 18:44
6.0?
#3
laowan0012022-01-25 19:41
VFP9 编译的EXE
#4
吹水佬2022-01-25 20:08
异常代码:c0000005
这个异常有点鬼异,是系统抛出的,VFP管不了
在2008运行是不是兼容问题,试设置为其他WIN版本兼容模式看看
#5
laowan0012022-01-25 20:39
以下是引用吹水佬在2022-1-25 20:08:00的发言:

异常代码:c0000005
这个异常有点鬼异,是系统抛出的,VFP管不了
在2008运行是不是兼容问题,试设置为其他WIN版本兼容模式看看

这种可能确实存在,后来也设置了winXP兼容模式观察
但是,这个EXE是自动更新的,有新版本的时候会删除旧的EXE,用新的同名EXE替代,不知这种情况下原来的兼容模式设置是否还有效
#6
laowan0012022-01-25 20:40
那个弹出的窗口有没有办法终止掉呢?
#7
吹水佬2022-01-25 21:07
以下是引用laowan001在2022-1-25 20:40:59的发言:

那个弹出的窗口有没有办法终止掉呢?

终止了也不能解决问题,应用程序应该是挂了
#8
foxfans2022-01-25 21:22
还是审查自己的代码,造成C5错误基本是代码不严谨,如内存泄漏,没有释放好,字符串拼接过长,还有vfp几个字符处理函数使用不当也会引起C5等,不要觉得on error try ....这些是万能的,更不要去怀疑系统底层常见库。
#9
laowan0012022-01-25 22:43
以下是引用吹水佬在2022-1-25 21:07:54的发言:


终止了也不能解决问题,应用程序应该是挂了

如果终止了,应用程序就闪退了,下一次就可以再次运行了
注:这种报错不是每次都有,应用程序是相同的。程序运行已经半年多了,最近才出现那个报错
#10
laowan0012022-01-25 22:50
以下是引用foxfans在2022-1-25 21:22:50的发言:

还是审查自己的代码,造成C5错误基本是代码不严谨,如内存泄漏,没有释放好,字符串拼接过长,还有vfp几个字符处理函数使用不当也会引起C5等,不要觉得on error try ....这些是万能的,更不要去怀疑系统底层常见库。


(1)内存泄漏应该不存在,以前吃过这个亏,所以很注意,并且每次都是由其他程序运行这个EXE,运行完就退出了
(2)字符串拼接有比较长的时候,大的时候有几M的字符串(SQL语句),但这样用了几年了,并没有出过问题
(3)您所说的几个字符处理函数具体是哪几个,使用不当是什么情况,麻烦说明一下,我也好对应检查,谢了!!!
#11
mywisdom882022-01-26 08:41
以下是引用laowan001在2022-1-25 20:39:58的发言:


这种可能确实存在,后来也设置了winXP兼容模式观察
但是,这个EXE是自动更新的,有新版本的时候会删除旧的EXE,用新的同名EXE替代,不知这种情况下原来的兼容模式设置是否还有效

这个是做什么用的?这个EXE是自动更新的上往云上传还是下载的,没必要几分种就执行一次吧
#12
laowan0012022-01-26 09:14
以下是引用mywisdom88在2022-1-26 08:41:09的发言:


这个是做什么用的?这个EXE是自动更新的上往云上传还是下载的,没必要几分种就执行一次吧

客户是物流企业,作业在随时进行,这个EXE是采集作业数据用的,所以几分钟就执行一次,方便前台查询作业进度及具体内容,属于准实时数据。
EXE更新机制:当程序调整后,编译新的EXE上传到服务器,服务器上的伺服程序发现有新版本,就删除旧EXE,下载新的EXE,继续执行
说明:EXE更新是根据需要进行的,有时一天能更新几次,有时几个月更新一次

[此贴子已经被作者于2022-1-26 09:16编辑过]

#13
mywisdom882022-01-26 12:39
那是,更新出问题还是采取数据出问题
#14
laowan0012022-01-26 13:32
以下是引用mywisdom88在2022-1-26 12:39:31的发言:

那是,更新出问题还是采取数据出问题

是采集数据时出的问题
#15
吹水佬2022-01-26 15:04
以下是引用laowan001在2022-1-25 20:39:58的发言:


这种可能确实存在,后来也设置了winXP兼容模式观察
但是,这个EXE是自动更新的,有新版本的时候会删除旧的EXE,用新的同名EXE替代,不知这种情况下原来的兼容模式设置是否还有效

一直在用,只是偶然出现的异常,应该可以排除“兼容”问题
#16
吹水佬2022-01-26 15:09
以下是引用laowan001在2022-1-25 22:50:25的发言:

(2)字符串拼接有比较长的时候,大的时候有几M的字符串(SQL语句),但这样用了几年了,并没有出过问题

字符串拼接有几M,大块头字串用来存放什么、具体是怎样操作的?
#17
吹水佬2022-01-26 15:18
以下是引用laowan001在2022-1-25 22:43:43的发言:


如果终止了,应用程序就闪退了,下一次就可以再次运行了
注:这种报错不是每次都有,应用程序是相同的。程序运行已经半年多了,最近才出现那个报错

最好能找出异常问题原因,才能彻底解决问题。
关闭弹出窗口:查找窗口获取窗口句柄,发送消息 WM_CLOSE or WM_DESTROY 给窗口
#18
吹水佬2022-01-26 15:22
偶然出现的问题也有可能是硬件的稳定性问题,如I/O读写异常问题(内存条、磁盘)。
#19
laowan0012022-01-26 16:24
以下是引用吹水佬在2022-1-26 15:09:58的发言:


字符串拼接有几M,大块头字串用来存放什么、具体是怎样操作的?

都是SQL语句,有时需要先得到查询结果,然后进行本地加工,之后再插入或更新后台数据库,大块头基本都是insert和update,多的时候会有上万条,所以会很大
#20
laowan0012022-01-26 16:24
以下是引用吹水佬在2022-1-26 15:22:57的发言:

偶然出现的问题也有可能是硬件的稳定性问题,如I/O读写异常问题(内存条、磁盘)。

确实没发现出错的规律
#21
laowan0012022-01-26 16:25
以下是引用吹水佬在2022-1-26 15:18:04的发言:


最好能找出异常问题原因,才能彻底解决问题。
关闭弹出窗口:查找窗口获取窗口句柄,发送消息 WM_CLOSE or WM_DESTROY 给窗口

如何获得窗口句柄,这是我的盲区,还请吹版指教。
另外,不知这种窗口的句柄每次都是一样的
#22
吹水佬2022-01-26 19:10
以下是引用laowan001在2022-1-26 16:25:58的发言:


如何获得窗口句柄,这是我的盲区,还请吹版指教。
另外,不知这种窗口的句柄每次都是一样的

API FindWindow()
句柄是会变的,FindWindow()返回窗口句柄,可按窗口标题查找,窗口标题不变就可以。
#23
吹水佬2022-01-26 19:16
以下是引用laowan001在2022-1-26 16:24:08的发言:


都是SQL语句,有时需要先得到查询结果,然后进行本地加工,之后再插入或更新后台数据库,大块头基本都是insert和update,多的时候会有上万条,所以会很大

上万条语句的串有点规模,可不可以分模块来处理,不用一次载入那么大的串,串太大也可能影响效率。
#24
laowan0012022-01-26 19:36
以下是引用吹水佬在2022-1-26 19:16:19的发言:


上万条语句的串有点规模,可不可以分模块来处理,不用一次载入那么大的串,串太大也可能影响效率。

所言极是,后来把可能较大的SQL加工成500K一次了
#25
laowan0012022-01-26 20:38
吹版好,现在可以获得窗口的句柄了,但是如何关闭还是没找到相应方法,似乎要通过PostMessage发送WM_CLOSE or WM_DESTROY,请吹版给段代码,谢谢!!!

再次感谢吹版,在坛里找吹版两年前的一个帖子,正好用到了这个,测试已经成功

[此贴子已经被作者于2022-1-26 20:54编辑过]

#26
吹水佬2022-01-26 20:56
回复 25楼 laowan001
DECLARE LONG SendMessage IN User32 LONG, LONG, LONG, LONG
#define WM_CLOSE    0x0010
SendMessage(窗口句柄, WM_CLOSE, 0, 0)
#27
laowan0012022-01-26 21:17
以下是引用吹水佬在2022-1-26 20:56:18的发言:

DECLARE LONG SendMessage IN User32 LONG, LONG, LONG, LONG
#define WM_CLOSE    0x0010
SendMessage(窗口句柄, WM_CLOSE, 0, 0)

OKOKOK
#28
foxfans2022-01-26 23:02
建议还是多测试代码,这里给你一个小工具,增加触发错误的EIP执行位置,通过反汇编可以找到相应的崩溃点(要有点破解基础),方便查看平时代码是否严谨,on error try捕获的错误级别有限. (附件见31楼)
只有本站会员才能查看附件,请 登录


[此贴子已经被作者于2022-1-27 18:08编辑过]

#29
laowan0012022-01-27 07:58
回复 28楼 foxfans
非常感谢,我试下
#30
吹水佬2022-01-27 15:52
试试用 API SetUnhandledExceptionFilter() 来处理系统异常情况。
不过VFP使用 SetUnhandledExceptionFilter() 涉及到回调函数(函数指针)的问题有点麻烦,可以用其他编程语言写API扩展库给VFP调用。
这样也许系统抛出异常时可以与VFP通信,VFP在出现异常关闭前能处理一些重要的事情。
#31
foxfans2022-01-27 16:12
对的,SEH, VEH类似,上面Dll采用 VEH+回调算出错误点的当前Eip地址(指针),通过当前EIP(32位),Hook计算该Eip指令所占用的长度(汇编指令长度)作为下一个忽略执行Eip起始点.

只有本站会员才能查看附件,请 登录

declare HookError in myErr.dll integer
HookError(1) &&开启后,要求主程序要写的很标准,一些小问题可能也会报出来(用来编译发布前的测试)写在load里面,一次加载即可

*!*模拟C5错误,正常情况主程序崩溃
FOR i=1 TO 15
SYS(1079,1)  &&通过HookError后,现在机会忽略处理,乘机"忽略"错误保存,再退出来查原因.
ENDFOR

*!* release
HookError(0)

只有本站会员才能查看附件,请 登录


[此贴子已经被作者于2022-1-27 17:57编辑过]

#32
吹水佬2022-01-28 21:57
回复 31楼 foxfans
SYS(2600,0,1," ")
这句抛出的异常有点鬼异,VFP也会抛出。

也简单写个异常处理(未详细测试)
只有本站会员才能查看附件,请 登录

只有本站会员才能查看附件,请 登录

程序代码:
#define MY_ERROR  0x401    && 自定义消息
DECLARE long AddException IN except long,long,long
DECLARE long RemoveException IN except
DECLARE long GetException IN except long
DECLARE long strlen IN msvcrt long
AddException(_screen.hWnd, MY_ERROR, 1)  && 0->直接忽略,1->提示选择
oErr = CREATEOBJECT("CError")
BINDEVENT(_screen.hWnd, MY_ERROR, oErr, "onError")
SYS(1079,1)
RemoveException()
CLEAR ALL
RETURN

DEFINE CLASS CError as Session
    PROCEDURE onError(hWnd,Msg,wParam,lParam)
        nCode = CTOBIN(SYS(2600,wParam,4),"4RS")
        pMsg = GetException(nCode)
        ? TRANSFORM(nCode,"@0"), SYS(2600,pMsg,strlen(pMsg))
        **QUIT
    ENDPROC
ENDDEFINE


[此贴子已经被作者于2022-1-28 21:59编辑过]

#33
laowan0012022-01-29 07:48
#34
schtg2022-01-29 08:29
高,学习啦,谢谢!
#35
吹水佬2022-01-29 10:05
修改一下
只有本站会员才能查看附件,请 登录

程序代码:
#define MY_ERROR  0x401    && 自定义消息
DECLARE long AddException IN except long,long,long
DECLARE long RemoveException IN except
DECLARE long GetException IN except long
DECLARE long strlen IN msvcrt long
** AddException(hWnd, msg, Flags) 注册异常处理
** 其中:Flags,0->直接忽略,1->提示选择
**   Flags=0 时会注销异常处理,直接忽略返回。
**   再要处理异常,要重新调用AddException()注册异常处理
AddException(_screen.hWnd, MY_ERROR, 0)  && 0->直接忽略,1->提示选择
oErr = CREATEOBJECT("CError")
BINDEVENT(_screen.hWnd, MY_ERROR, oErr, "onError")
SYS(1079,1)
AddException(_screen.hWnd, MY_ERROR, 1)
SYS(2600,0,1," ")
RemoveException()
CLEAR ALL
RETURN

DEFINE CLASS CError as Session
    PROCEDURE onError(hWnd,Msg,wParam,lParam)
        nCode = CTOBIN(SYS(2600,wParam,4),"4RS")
        pMsg = GetException(nCode)
        ? TRANSFORM(nCode,"@0"), SYS(2600,pMsg,strlen(pMsg))
        **QUIT
    ENDPROC
ENDDEFINE



[此贴子已经被作者于2022-1-29 10:11编辑过]

#36
laowan0012022-01-29 10:11
回复 35楼 吹水佬
这是针对程序自身的异常处理吧?
#37
吹水佬2022-01-29 10:14
以下是引用laowan001在2022-1-29 10:11:28的发言:

这是针对程序自身的异常处理吧?

捕捉异常,有些异常VFP捕捉不了,也许他可以。

[此贴子已经被作者于2022-1-29 10:15编辑过]

#38
laowan0012022-01-29 11:04
回复 37楼 吹水佬
对其他EXE的异常也能捕捉?
#39
吹水佬2022-01-29 11:15
以下是引用laowan001在2022-1-29 11:04:03的发言:

对其他EXE的异常也能捕捉?

本示例DLL只在本进程应用。
如果要跨进程使用,要将DLL注入外部进程,这是另一个问题,也不清楚这样做有什么意义。
#40
foxfans2022-01-29 11:40
主要目的就是跳过忽略错误,方便查看错误和异常位置,且有机会介入保存。sys(2600,0...)空指针,可以手动让eip+1即可跳过,一直忽略也不是办法,所以可以直接计算当前eip的内码长度,eip+这个长度,就直接跳过整块代码段,不用一直忽略.

[此贴子已经被作者于2022-1-29 11:43编辑过]

#41
laowan0012022-01-29 13:44
回复 39楼 吹水佬
明白了,谢谢
#42
吹水佬2022-01-29 19:15
以下是引用foxfans在2022-1-29 11:40:19的发言:

可以手动让eip+1即可跳过,一直忽略也不是办法,所以可以直接计算当前eip的内码长度,eip+这个长度,就直接跳过整块代码段,不用一直忽略.

指令长度,静态分析就可以看反编。动态分析就有点麻烦,指令长度与指令、操作数、寻址方式等有关,同一条指令但操作数用不同的寻址方式,其指令长度就会不一样,这样就要尽可能多地获取EIP的指令字节数来推算。

现在的问题是异常处理,已经抛出异常了,将EIP指向下一条指令也有可能继续抛出异常。既然是选择手动“忽略”,就让他忽略下去好了。
如果选择自动忽略,应该就可以解决LZ要关闭那个弹出窗口的问题。

#43
吹水佬2022-01-29 22:12
有个Debugger的代码可以参考一下
代码链接  http://pan.baidu.com/s/1qXYKjxU
其中\Debugger2\Debugger1\DeAsmEngine.cpp有个获取指令长度函数 GetCoodeLen
程序代码:
//\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/
// 函数名: GetCoodeLen
///功  能: 获取指令长度
// 形  参: HANDLE g_hCurrentProcess
// 形  参: const LPVOID lpAddress
//\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/
int GetCoodeLen(const SIZE_T lpAddress)
{
    // 1. 将调试程序的内存(OPCode)复制到本地
    SIZE_T  dwRetSize = 0;
    LPVOID lpRemote_Buf = new BYTE[32];
    ZeroMemory(lpRemote_Buf,32);
    ReadProcessMemory(g_hProcess,(LPVOID)lpAddress,lpRemote_Buf,32,&dwRetSize);

    // 2. 初始化反汇编引擎
    DISASM objDiasm;
    objDiasm.EIP = (UIntPtr)lpRemote_Buf; // 起始地址
    objDiasm.VirtualAddr = (UINT64)lpAddress;     // 虚拟内存地址(反汇编引擎用于计算地址)
    objDiasm.Archi = 0;                     // AI-X86
    objDiasm.Options = 0x000;                 // MASM

    // 3. 反汇编代码
    int nLen = Disasm(&objDiasm);
    delete[] lpRemote_Buf;
    return nLen;
}

#44
吹水佬2022-01-31 12:09
看了一下获取指令长度的算法,参考人家的代码再改改,不断完善吧
只有本站会员才能查看附件,请 登录

只有本站会员才能查看附件,请 登录

程序代码:
#define MY_ERROR  0x401    && 自定义消息
DECLARE long AddException IN except long,long,long
DECLARE long RemoveException IN except
DECLARE long GetException IN except long
DECLARE long strlen IN msvcrt long
AddException(_screen.hWnd, MY_ERROR, 1)  && 0->全部忽略,1->提示选择
oErr = CREATEOBJECT("CError")
BINDEVENT(_screen.hWnd, MY_ERROR, oErr, "onError")
SYS(1079,1)
? 1234
SYS(2600,0,1," ")
? 5678
RemoveException()
CLEAR ALL
RETURN

DEFINE CLASS CError as Session
    PROCEDURE onError(hWnd,Msg,wParam,lParam)  && wParam指向EXCEPTION_RECORD结构的指针
        nCode = CTOBIN(SYS(2600,wParam,4),"4RS")
        pMsg = GetException(nCode)
        ? TRANSFORM(nCode,"@0"), SYS(2600,pMsg,strlen(pMsg))
        **QUIT
    ENDPROC
ENDDEFINE


[此贴子已经被作者于2022-1-31 14:16编辑过]

#45
schtg2022-01-31 19:45
回复 44楼 吹水佬
谢谢,新年快乐!
#46
吹水佬2022-02-03 11:53
疫情反复无常,1月8号、2月2号买了车票上北京都被劝回不给进京,只好呆在家练练手。
只有本站会员才能查看附件,请 登录

只有本站会员才能查看附件,请 登录

程序代码:

cDefPath = ADDBS(JUSTPATH(SYS(16)))
SET DEFAULT TO (cDefPath)
#define MY_ERROR  0x401    && 自定义消息
DECLARE long AddException IN errdbg long,string@,long,long
DECLARE long RemoveException IN errdbg
DECLARE long GetException IN errdbg long
DECLARE long strlen IN msvcrt long
** AddException(nType, cOutFile, hWnd, uMsg) 注册异常处理
** 参数:
**   nType ..... 0->全部忽略跳过,1->提示选择中止、重试、忽略
**   cOutFile .. 输出异常信息的文件名,为空不输出
**   hWnd ...... 接收异常信息的窗口句柄,为 0 不接收
**   uMsg ...... 发送异常信息的用户自定义消息,为 0 不发送
AddException(1, cDefPath+"errinfo.txt", _screen.hWnd, MY_ERROR)
oErr = CREATEOBJECT("CError")
BINDEVENT(_screen.hWnd, MY_ERROR, oErr, "onError")
SYS(1079,1)
? 1111
fun1()
RemoveException()  && 注销异常处理
CLEAR ALL
RETURN

FUNCTION fun1()
    SYS(1079,1)
    ? 2222
    fun2()
ENDFUNC

FUNCTION fun2()
    SYS(2600,0,1," ")
    ? 3333  
ENDFUNC

DEFINE CLASS CError as Session
    PROCEDURE onError(hWnd,Msg,wParam,lParam)  && wParam指向EXCEPTION_RECORD结构的指针
        nCode = CTOBIN(SYS(2600,wParam,4),"4RS")
        pMsg = GetException(nCode)
        ? TRANSFORM(nCode,"@0"), SYS(2600,pMsg,strlen(pMsg))
        FOR i=1 TO PROGRAM(-1)-1
            ? SPACE(i),PROGRAM(i)
        ENDFOR
        **QUIT
    ENDPROC
ENDDEFINE


[此贴子已经被作者于2022-2-3 19:44编辑过]

#47
laowan0012022-02-03 19:45
#48
schtg2022-02-04 06:52
谢谢!
#49
kangss2022-02-10 14:56
十分感谢!
1