如何将exe异常路由回VB6应用程序?
我有一个 vb6 应用程序,它将调用 mencoder.exe,它是 mplayer 的一部分,用于将某些文件转换为 flv 格式。 每当我尝试转换这个 opendivx 文件时,我都会从 mencoder 收到这个奇怪的未处理异常问题。
目前,我不清楚这个编解码器是否是这背后的罪魁祸首。 不管怎样,我都尝试修改命令行,甚至下载了 mencoder 的最新可用版本。
所以转换工作正常,唯一的问题是 mencoder 最终会崩溃,因为视频文件不知何故超过 100% 到 102%。 因此我的问题是如何路由此异常由我的 vb6 应用程序处理,以便不会显示丑陋的错误弹出窗口?
我什至在代码中包含了异常捕获,但它没有捕获该异常。
' Function GetCommandOutput
'
' sCommandLine: [in] Command line to launch
' blnStdOut [in,opt] True (defualt) to capture output to STDOUT
' blnStdErr [in,opt] True to capture output to STDERR. False is default.
' blnOEMConvert: [in,opt] True (default) to convert DOS characters to Windows, False to skip conversion
'
' Returns: String with STDOUT and/or STDERR output
'
Public Function GetCommandOutput(sCommandLine As String, _
Optional blnStdOut As Boolean = True, _
Optional blnStdErr As Boolean = False, _
Optional blnOEMConvert As Boolean = True, _
Optional encoderType As String) As String
Dim hPipeRead As Long, hPipeWrite1 As Long, hPipeWrite2 As Long
Dim hCurProcess As Long
Dim sa As SECURITY_ATTRIBUTES
Dim si As STARTUPINFO
Dim pi As PROCESS_INFORMATION
Dim baOutput() As Byte
Dim sNewOutPut As String
Dim lBytesRead As Long
Dim fTwoHandles As Boolean
Dim lRet As Long
Const BUFSIZE = 1024 ' pipe buffer size
On Error GoTo ErrorHandler
' At least one of them should be True, otherwise there's no point in calling the function
If (Not blnStdOut) And (Not blnStdErr) Then
Err.Raise 5 ' Invalid Procedure call or Argument
End If
' If both are true, we need two write handles. If not, one is enough.
fTwoHandles = blnStdOut And blnStdErr
ReDim baOutput(BUFSIZE - 1) As Byte
With sa
.nLength = Len(sa)
.bInheritHandle = 1 ' get inheritable pipe handles
End With
If CreatePipe(hPipeRead, hPipeWrite1, sa, BUFSIZE) = 0 Then
Exit Function
End If
hCurProcess = GetCurrentProcess()
' Replace our inheritable read handle with an non-inheritable. Not that it
' seems to be necessary in this case, but the docs say we should.
Call DuplicateHandle(hCurProcess, hPipeRead, hCurProcess, hPipeRead, 0&, 0&, DUPLICATE_SAME_ACCESS Or DUPLICATE_CLOSE_SOURCE)
' If both STDOUT and STDERR should be redirected, get an extra handle.
If fTwoHandles Then
Call DuplicateHandle(hCurProcess, hPipeWrite1, hCurProcess, hPipeWrite2, 0&, 1&, DUPLICATE_SAME_ACCESS)
End If
With si
.cb = Len(si)
.dwFlags = STARTF_USESHOWWINDOW Or STARTF_USESTDHANDLES
.wShowWindow = SW_HIDE ' hide the window
If fTwoHandles Then
.hStdOutput = hPipeWrite1
.hStdError = hPipeWrite2
ElseIf blnStdOut Then
.hStdOutput = hPipeWrite1
Else
.hStdError = hPipeWrite1
End If
End With
Dim totalSeconds As Double
If CreateProcess(vbNullString, sCommandLine, ByVal 0&, ByVal 0&, 1, 0&, ByVal 0&, vbNullString, si, pi) Then
' Close thread handle - we don't need it
Call CloseHandle(pi.hThread)
' Also close our handle(s) to the write end of the pipe. This is important, since
' ReadFile will *not* return until all write handles are closed or the buffer is full.
Call CloseHandle(hPipeWrite1)
hPipeWrite1 = 0
If hPipeWrite2 Then
Call CloseHandle(hPipeWrite2)
hPipeWrite2 = 0
End If
Do
' Add a DoEvents to allow more data to be written to the buffer for each call.
' This results in fewer, larger chunks to be read.
'DoEvents
If ReadFile(hPipeRead, baOutput(0), BUFSIZE, lBytesRead, ByVal 0&) = 0 Then
Exit Do
End If
If blnOEMConvert Then
' convert from "DOS" to "Windows" characters
sNewOutPut = String$(lBytesRead, 0)
Call OemToCharBuff(baOutput(0), sNewOutPut, lBytesRead)
Else
' perform no conversion (except to Unicode)
sNewOutPut = Left$(StrConv(baOutput(), vbUnicode), lBytesRead)
End If
GetCommandOutput = GetCommandOutput & sNewOutPut
' If you are executing an application that outputs data during a long time,
' and don't want to lock up your application, it might be a better idea to
' wrap this code in a class module in an ActiveX EXE and execute it asynchronously.
' Then you can raise an event here each time more data is available.
'Debug.Print sNewOutPut + vbNewLine
If encoderType = "ffmpeg" Then
If totalSeconds < 1 Then
totalSeconds = GetFFmpegFileTotalSeconds(sNewOutPut)
End If
Call CalculateFFMpegProgress(sNewOutPut, totalSeconds)
Else
Call CalculateMencoderProgress(sNewOutPut)
End If
'RaiseEvent OutputAvailable(sNewOutput)
Loop
' When the process terminates successfully, Err.LastDllError will be
' ERROR_BROKEN_PIPE (109). Other values indicates an error.
Call CloseHandle(pi.hProcess)
Else
GetCommandOutput = "Failed to create process, check the path of the command line."
End If
' clean up
Call CloseHandle(hPipeRead)
If hPipeWrite1 Then
Call CloseHandle(hPipeWrite1)
End If
If hPipeWrite2 Then
Call CloseHandle(hPipeWrite2)
End If
Exit Function
ErrorHandler:
Call WriteErrorLog(Err, "Class clsThread : Sub GetCommandOutput")
End Function
更新:
如果你们好奇应用程序崩溃时输出了什么,这里是:
1 个重复帧!
Pos: 83.2s 2504f (99%) 112.65fps Trem: 0min 6mb AV:0.008 [571:79]] 1 个重复帧!
Pos: 83.4s 2510f (102%) 112.74fps Trem: 0min 6mb AV:0.006 [571:79] 1 个重复帧!
Pos: 83.6s 2516f (102%) 112.84fps Trem: 0min 6mb AV:0.004 [571:79] 1 个重复帧!
[mpeg4 @ 0x1ac53a0]隐藏 40 DC、40 AC、40 MV 错误 AV:0.003 [571:79]
替代文本 http://img21.imageshack.us/img21/4539/exception.png
谢谢:)
i have a vb6 apps which will call the mencoder.exe which is part of the mplayer to convert some files into flv format. i'm getting this weird unhandled exception problem from mencoder whenever i try to convert this one opendivx file.
At the moment, i'm unclear of whether is this codec is the culprit behind this. Either way i have try to modify the command line and even downloaded the latest available version for mencoder.
so the conversion works fine and the only problem is that the mencoder will crash in the end, as the video file somehow exceeds 100% to 102%. thus my question is how do i route this exception to be handled by my vb6 app so that the ugly error popup won't be shown?
i've even included the exception capture in the code, but it's not catching that exception.
' Function GetCommandOutput
'
' sCommandLine: [in] Command line to launch
' blnStdOut [in,opt] True (defualt) to capture output to STDOUT
' blnStdErr [in,opt] True to capture output to STDERR. False is default.
' blnOEMConvert: [in,opt] True (default) to convert DOS characters to Windows, False to skip conversion
'
' Returns: String with STDOUT and/or STDERR output
'
Public Function GetCommandOutput(sCommandLine As String, _
Optional blnStdOut As Boolean = True, _
Optional blnStdErr As Boolean = False, _
Optional blnOEMConvert As Boolean = True, _
Optional encoderType As String) As String
Dim hPipeRead As Long, hPipeWrite1 As Long, hPipeWrite2 As Long
Dim hCurProcess As Long
Dim sa As SECURITY_ATTRIBUTES
Dim si As STARTUPINFO
Dim pi As PROCESS_INFORMATION
Dim baOutput() As Byte
Dim sNewOutPut As String
Dim lBytesRead As Long
Dim fTwoHandles As Boolean
Dim lRet As Long
Const BUFSIZE = 1024 ' pipe buffer size
On Error GoTo ErrorHandler
' At least one of them should be True, otherwise there's no point in calling the function
If (Not blnStdOut) And (Not blnStdErr) Then
Err.Raise 5 ' Invalid Procedure call or Argument
End If
' If both are true, we need two write handles. If not, one is enough.
fTwoHandles = blnStdOut And blnStdErr
ReDim baOutput(BUFSIZE - 1) As Byte
With sa
.nLength = Len(sa)
.bInheritHandle = 1 ' get inheritable pipe handles
End With
If CreatePipe(hPipeRead, hPipeWrite1, sa, BUFSIZE) = 0 Then
Exit Function
End If
hCurProcess = GetCurrentProcess()
' Replace our inheritable read handle with an non-inheritable. Not that it
' seems to be necessary in this case, but the docs say we should.
Call DuplicateHandle(hCurProcess, hPipeRead, hCurProcess, hPipeRead, 0&, 0&, DUPLICATE_SAME_ACCESS Or DUPLICATE_CLOSE_SOURCE)
' If both STDOUT and STDERR should be redirected, get an extra handle.
If fTwoHandles Then
Call DuplicateHandle(hCurProcess, hPipeWrite1, hCurProcess, hPipeWrite2, 0&, 1&, DUPLICATE_SAME_ACCESS)
End If
With si
.cb = Len(si)
.dwFlags = STARTF_USESHOWWINDOW Or STARTF_USESTDHANDLES
.wShowWindow = SW_HIDE ' hide the window
If fTwoHandles Then
.hStdOutput = hPipeWrite1
.hStdError = hPipeWrite2
ElseIf blnStdOut Then
.hStdOutput = hPipeWrite1
Else
.hStdError = hPipeWrite1
End If
End With
Dim totalSeconds As Double
If CreateProcess(vbNullString, sCommandLine, ByVal 0&, ByVal 0&, 1, 0&, ByVal 0&, vbNullString, si, pi) Then
' Close thread handle - we don't need it
Call CloseHandle(pi.hThread)
' Also close our handle(s) to the write end of the pipe. This is important, since
' ReadFile will *not* return until all write handles are closed or the buffer is full.
Call CloseHandle(hPipeWrite1)
hPipeWrite1 = 0
If hPipeWrite2 Then
Call CloseHandle(hPipeWrite2)
hPipeWrite2 = 0
End If
Do
' Add a DoEvents to allow more data to be written to the buffer for each call.
' This results in fewer, larger chunks to be read.
'DoEvents
If ReadFile(hPipeRead, baOutput(0), BUFSIZE, lBytesRead, ByVal 0&) = 0 Then
Exit Do
End If
If blnOEMConvert Then
' convert from "DOS" to "Windows" characters
sNewOutPut = String$(lBytesRead, 0)
Call OemToCharBuff(baOutput(0), sNewOutPut, lBytesRead)
Else
' perform no conversion (except to Unicode)
sNewOutPut = Left$(StrConv(baOutput(), vbUnicode), lBytesRead)
End If
GetCommandOutput = GetCommandOutput & sNewOutPut
' If you are executing an application that outputs data during a long time,
' and don't want to lock up your application, it might be a better idea to
' wrap this code in a class module in an ActiveX EXE and execute it asynchronously.
' Then you can raise an event here each time more data is available.
'Debug.Print sNewOutPut + vbNewLine
If encoderType = "ffmpeg" Then
If totalSeconds < 1 Then
totalSeconds = GetFFmpegFileTotalSeconds(sNewOutPut)
End If
Call CalculateFFMpegProgress(sNewOutPut, totalSeconds)
Else
Call CalculateMencoderProgress(sNewOutPut)
End If
'RaiseEvent OutputAvailable(sNewOutput)
Loop
' When the process terminates successfully, Err.LastDllError will be
' ERROR_BROKEN_PIPE (109). Other values indicates an error.
Call CloseHandle(pi.hProcess)
Else
GetCommandOutput = "Failed to create process, check the path of the command line."
End If
' clean up
Call CloseHandle(hPipeRead)
If hPipeWrite1 Then
Call CloseHandle(hPipeWrite1)
End If
If hPipeWrite2 Then
Call CloseHandle(hPipeWrite2)
End If
Exit Function
ErrorHandler:
Call WriteErrorLog(Err, "Class clsThread : Sub GetCommandOutput")
End Function
Updates:
and in case you guys are curious what the application was outputting when it crash, here it is:
1 duplicate frame(s)!
Pos: 83.2s 2504f (99%) 112.65fps Trem: 0min 6mb A-V:0.008 [571:79]]
1 duplicate frame(s)!
Pos: 83.4s 2510f (102%) 112.74fps Trem: 0min 6mb A-V:0.006 [571:79]
1 duplicate frame(s)!
Pos: 83.6s 2516f (102%) 112.84fps Trem: 0min 6mb A-V:0.004 [571:79]
1 duplicate frame(s)!
[mpeg4 @ 0x1ac53a0]concealing 40 DC, 40 AC, 40 MV errors A-V:0.003 [571:79]
alt text http://img21.imageshack.us/img21/4539/exception.png
thanks :)
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
![扫码二维码加入Web技术交流群](/public/img/jiaqun_03.jpg)
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
您可以尝试使用 SetUnhandledExceptionFilter API 来捕获异常。 我以前使用过它,但只取得了有限的成功。
我相信这段代码最初要么来自 Jonathan Lunman 发表在 99 年 5 月 Visual Basic 程序员杂志上的一篇名为“No Exception Errors, My Dear Dr. Watson”的文章,要么深受其影响,要么受到其启发。
查看 MSDN 上的 SetUnhandledExceptionFilter 函数了解更多信息信息。
You could try using the SetUnhandledExceptionFilter API to catch the exception. I've used it previously, but only with limited success.
I believe this code originally either came from, was heavily influenced by, or was inspired by, an Article in the May 99 Visual Basic Programmers Journal called "No Exception Errors, My Dear Dr. Watson" by Jonathan Lunman.
Check out the SetUnhandledExceptionFilter Function at MSDN for more info.
如果此应用程序在 Windows XP 或更高版本下运行,请尝试使用向量异常处理。 您可以编写一组简单的 C++ DLL 或使用 API 调用来捕获 Win32 API 中发生的各种活动。
MSDN 文章
If this application is running under Windows XP or later try looking at using Vectored Exception Handling. You are able to write a simple set of C++ DLLs or use API Calls to trap various activities occurring in the Win32 API.
MSDN Article