Multithreading VB6外壳进程在继续主线程之前等待客户端

Multithreading VB6外壳进程在继续主线程之前等待客户端,multithreading,vb6,Multithreading,Vb6,我想创建一个应用程序来启动CMD或MySQL控制台之类的进程,并向其输入命令,然后读取输出,而不使用文件 当我需要使用ReadAllOutput()时,我从stackoverflow获得的代码会一直挂起主线程,即使在我关闭客户端线程之后,主线程也不会收到来自客户端的输出 我需要保持客户端线程处于活动状态,因为我需要登录到某些系统 以下是我正在使用的类: Option Explicit DefObj A-Z Private Const MODULE_NAME As String = "cExec"

我想创建一个应用程序来启动CMD或MySQL控制台之类的进程,并向其输入命令,然后读取输出,而不使用文件

当我需要使用
ReadAllOutput()
时,我从stackoverflow获得的代码会一直挂起主线程,即使在我关闭客户端线程之后,主线程也不会收到来自客户端的输出

我需要保持客户端线程处于活动状态,因为我需要登录到某些系统

以下是我正在使用的类:

Option Explicit
DefObj A-Z
Private Const MODULE_NAME As String = "cExec"

'=========================================================================
' API
'=========================================================================

'--- for CreateProcess
Private Const STARTF_USESHOWWINDOW          As Long = 1
Private Const STARTF_USESTDHANDLES          As Long = &H100
Private Const SW_HIDE                       As Long = 0
Private Const SW_MINIMIZE                   As Long = 6
Private Const NORMAL_PRIORITY_CLASS         As Long = &H20&
'--- for WaitForSingleObject
Private Const INFINITE                      As Long = &HFFFFFFFF
'--- for DuplicateHandle
Private Const DUPLICATE_SAME_ACCESS         As Long = &H2
'--- for GetExitCodeProcess
Private Const STATUS_PENDING                As Long = &H103

Private Declare Function CreateProcessA Lib "kernel32" (ByVal lpApplicationName As String, ByVal lpCommandLine As String, ByVal lpProcessAttributes As Long, ByVal lpThreadAttributes As Long, ByVal bInheritHandles As Long, ByVal dwCreationFlags As Long, ByVal lpEnvironment As Long, ByVal lpCurrentDirectory As String, lpStartupInfo As STARTUPINFO, lpProcessInformation As PROCESS_INFORMATION) As Long
Private Declare Function WaitForSingleObject Lib "kernel32" (ByVal hHandle As Long, ByVal dwMilliseconds As Long) As Long
Private Declare Function GetExitCodeProcess Lib "kernel32" (ByVal hProcess As Long, lpExitCode As Long) As Long
Private Declare Function CloseHandle Lib "kernel32" (ByVal hObject As Long) As Long
Private Declare Function CreatePipe Lib "kernel32" (phReadPipe As Long, phWritePipe As Long, lpPipeAttributes As SECURITY_ATTRIBUTES, ByVal nSize As Long) As Long
Private Declare Function GetCurrentProcess Lib "kernel32" () As Long
Private Declare Function DuplicateHandle Lib "kernel32" (ByVal hSourceProcessHandle As Long, ByVal hSourceHandle As Long, ByVal hTargetProcessHandle As Long, lpTargetHandle As Long, ByVal dwDesiredAccess As Long, ByVal bInheritHandle As Long, ByVal dwOptions As Long) As Long
Private Declare Function ReadFile Lib "kernel32" (ByVal hFile As Long, lpBuffer As Any, ByVal nNumberOfBytesToRead As Long, lpNumberOfBytesRead As Long, ByVal lpOverlapped As Long) As Long
Private Declare Function TerminateProcess Lib "kernel32" (ByVal hProcess As Long, ByVal uExitCode As Long) As Long
Private Declare Function WriteFile Lib "kernel32" (ByVal hFile As Long, lpBuffer As Any, ByVal nNumberOfBytesToWrite As Long, lpNumberOfBytesWritten As Long, ByVal lpOverlapped As Long) As Long
Private Declare Function FlushFileBuffers Lib "kernel32" (ByVal hFile As Long) As Long
Private Declare Function PeekNamedPipe Lib "kernel32" (ByVal hNamedPipe As Long, lpBuffer As Any, ByVal nBufferSize As Long, lpBytesRead As Long, lpTotalBytesAvail As Long, lpBytesLeftThisMessage As Long) As Long

Private Type STARTUPINFO
    cb                  As Long
    lpReserved          As String
    lpDesktop           As String
    lpTitle             As String
    dwX                 As Long
    dwY                 As Long
    dwXSize             As Long
    dwYSize             As Long
    dwXCountChars       As Long
    dwYCountChars       As Long
    dwFillAttribute     As Long
    dwFlags             As Long
    wShowWindow         As Integer
    cbReserved2         As Integer
    lpReserved2         As Long
    hStdInput           As Long
    hStdOutput          As Long
    hStdError           As Long
End Type

Private Type PROCESS_INFORMATION
    hProcess            As Long
    hThread             As Long
    dwProcessId         As Long
    dwThreadId          As Long
End Type

Private Type SECURITY_ATTRIBUTES
    nLength             As Long
    lpSecurityDescriptor As Long
    bInheritHandle      As Long
End Type

'=========================================================================
' Constants and member variables
'=========================================================================

Private m_hProcess          As Long
Private m_hReadOutput       As Long
Private m_hReadError        As Long
Private m_hWriteInput       As Long

'=========================================================================
' Error handling
'=========================================================================

Private Sub PrintError(sFunc As String)
    MsgBox Error & " in " & MODULE_NAME & "." & sFunc & "(" & Erl & ")", vbCritical, App.EXEName
End Sub

'=========================================================================
' Methods
'=========================================================================

Public Function Run( _
            sFile As String, _
            sParams As String, _
            Optional ByVal bStartHidden As Boolean, _
            Optional ByVal bStartMimized As Boolean) As Boolean
    Const FUNC_NAME     As String = "Run"
    Dim uProcInfo       As PROCESS_INFORMATION
    Dim uStart          As STARTUPINFO
    Dim sCommandLine    As String
    Dim uSA             As SECURITY_ATTRIBUTES
    Dim hTmp            As Long
    Dim hWriteOutput    As Long
    Dim hWriteError     As Long
    Dim hReadInput      As Long

    On Error GoTo EH
    '--- cleanup previous
    If m_hProcess <> 0 Then
        Call CloseHandle(m_hProcess)
        m_hProcess = 0
    End If
    If m_hReadOutput <> 0 Then
        Call CloseHandle(m_hReadOutput)
        m_hReadOutput = 0
    End If
    If m_hReadError <> 0 Then
        Call CloseHandle(m_hReadError)
        m_hReadError = 0
    End If
    '--- win9x: fix spaces or not working on 9X
    If InStr(sFile, " ") > 0 And Left$(sFile, 1) <> """" Then
        sCommandLine = """" & sFile & """" & " " & sParams
    Else
        sCommandLine = sFile & " " & sParams
    End If
    '--- create pipes
    uSA.nLength = Len(uSA)
    uSA.bInheritHandle = 1
    Call CreatePipe(hTmp, hWriteOutput, uSA, 0)
    Call DuplicateHandle(GetCurrentProcess(), hTmp, GetCurrentProcess(), m_hReadOutput, 0, False, DUPLICATE_SAME_ACCESS)
    Call CloseHandle(hTmp)
    Call CreatePipe(hTmp, hWriteError, uSA, 0)
    Call DuplicateHandle(GetCurrentProcess(), hTmp, GetCurrentProcess(), m_hReadError, 0, False, DUPLICATE_SAME_ACCESS)
    Call CloseHandle(hTmp)
    Call CreatePipe(hReadInput, hTmp, uSA, 0)
    Call DuplicateHandle(GetCurrentProcess(), hTmp, GetCurrentProcess(), m_hWriteInput, 0, False, DUPLICATE_SAME_ACCESS)
    Call CloseHandle(hTmp)
    '--- setup start info
    uStart.cb = Len(uStart)
    uStart.dwFlags = STARTF_USESTDHANDLES
    uStart.hStdInput = hReadInput  ' GetStdHandle(STD_INPUT_HANDLE)
    uStart.hStdOutput = hWriteOutput
    uStart.hStdError = hWriteError
    If bStartHidden Then
        uStart.dwFlags = uStart.dwFlags Or STARTF_USESHOWWINDOW
        uStart.wShowWindow = SW_HIDE
    End If
    If bStartMimized Then
        uStart.dwFlags = uStart.dwFlags Or STARTF_USESHOWWINDOW
        uStart.wShowWindow = SW_MINIMIZE
    End If
    If CreateProcessA(vbNullString, sCommandLine, 0, 0, 1, NORMAL_PRIORITY_CLASS, 0, vbNullString, uStart, uProcInfo) <> 0 Then
        Call CloseHandle(uProcInfo.hThread)
        m_hProcess = uProcInfo.hProcess
    Else
        Call CloseHandle(m_hReadOutput)
        m_hReadOutput = 0
        Call CloseHandle(m_hReadError)
        m_hReadError = 0
    End If
    Call CloseHandle(hWriteOutput)
    Call CloseHandle(hWriteError)
    '--- success (or failure)
    Run = (m_hProcess <> 0)
    Exit Function
EH:
    PrintError FUNC_NAME
End Function

Public Function AtEndOfError() As Boolean
    Dim lTotal2         As Long

    If m_hReadError <> 0 Then
        If PeekNamedPipe(m_hReadError, ByVal 0, 0, 0, lTotal2, 0) = 0 Then
            Call CloseHandle(m_hReadError)
            m_hReadError = 0
        End If
    End If
    AtEndOfError = (m_hReadError = 0)
End Function

Public Function ReadError(ByVal lSize As Long) As String
    Dim szBuffer        As String
    Dim lRead           As Long

    If m_hReadError <> 0 Then
        szBuffer = String(lSize, 0)
        If ReadFile(m_hReadError, ByVal szBuffer, lSize, lRead, 0) <> 0 Then
            ReadError = Left$(szBuffer, lSize)
        Else
            Call CloseHandle(m_hReadError)
            m_hReadError = 0
        End If
    End If
End Function

Public Function ReadLineError(Optional sTerminator As String) As String
    Do While Not AtEndOfError
        ReadLineError = ReadLineError & ReadError(1)
        If Right$(ReadLineError, 2) = vbCrLf Then
            Exit Function
        ElseIf LenB(sTerminator) <> 0 And Right$(ReadLineError, Len(sTerminator)) = sTerminator Then
            Exit Function
        End If
        DoEvents
    Loop
End Function

Public Function ReadAllError() As String
    Do While Not AtEndOfError
        ReadAllError = ReadAllError & ReadError(100)
        DoEvents
    Loop
End Function

Public Function ReadPendingError() As String
    Dim lTotal          As Long

    Do While Not AtEndOfError
        lTotal = 0
        If PeekNamedPipe(m_hReadError, ByVal 0, 0, 0, lTotal, 0) = 0 Then
            Call CloseHandle(m_hReadError)
            m_hReadError = 0
        End If
        If lTotal > 0 Then
            ReadPendingError = ReadPendingError & ReadError(lTotal)
        Else
            Exit Function
        End If
        DoEvents
    Loop
End Function

Public Function AtEndOfOutput() As Boolean
    Dim lTotal2         As Long

    If m_hReadOutput <> 0 Then
        If PeekNamedPipe(m_hReadOutput, ByVal 0, 0, 0, lTotal2, 0) = 0 Then
            Call CloseHandle(m_hReadOutput)
            m_hReadOutput = 0
        End If
    End If
    AtEndOfOutput = (m_hReadOutput = 0)
End Function

Public Function ReadOutput(ByVal lSize As Long) As String
    Dim szBuffer        As String
    Dim lRead           As Long

    If m_hReadOutput <> 0 Then
        szBuffer = String(lSize, 0)
        If ReadFile(m_hReadOutput, ByVal szBuffer, lSize, lRead, 0) <> 0 Then
            ReadOutput = Left$(szBuffer, lSize)
        Else
            Call CloseHandle(m_hReadOutput)
            m_hReadOutput = 0
        End If
    End If
End Function

Public Function ReadLineOutput() As String
    Do While Not AtEndOfOutput
        ReadLineOutput = ReadLineOutput & ReadOutput(1)
        If Right$(ReadLineOutput, 2) = vbCrLf Then
            Exit Function
        End If
        DoEvents
    Loop
End Function

Public Function ReadAllOutput() As String
    Do While Not AtEndOfOutput
        ReadAllOutput = ReadAllOutput & ReadOutput(100)
        DoEvents
    Loop
End Function

Public Function ReadPendingOutput() As String
    Dim lTotal          As Long

    Do While Not AtEndOfOutput
        lTotal = 0
        If PeekNamedPipe(m_hReadOutput, ByVal 0, 0, 0, lTotal, 0) = 0 Then
            Call CloseHandle(m_hReadOutput)
            m_hReadOutput = 0
        End If
        If lTotal > 0 Then
            ReadPendingOutput = ReadPendingOutput & ReadOutput(lTotal)
        Else
            Exit Function
        End If
        DoEvents
    Loop
End Function

Public Function WriteInput(sValue As String) As Boolean
    Dim lWritten        As Long
    DoEvents
    If m_hWriteInput <> 0 Then
        If WriteFile(m_hWriteInput, ByVal sValue, Len(sValue), lWritten, 0) <> 0 Then
            DoEvents
            'Call FlushFileBuffers(m_hWriteInput)
            WriteInput = True
        Else
            WriteInput = False
        End If
    End If
End Function

Public Function GetExitCode() As Long
    If m_hProcess <> 0 Then
        Call GetExitCodeProcess(m_hProcess, GetExitCode)
        If GetExitCode = STATUS_PENDING Then
            If m_hReadOutput <> 0 Then
                Call CloseHandle(m_hReadOutput)
                m_hReadOutput = 0
            End If
            If m_hReadError <> 0 Then
                Call CloseHandle(m_hReadError)
                m_hReadError = 0
            End If
            If m_hWriteInput <> 0 Then
                Call CloseHandle(m_hWriteInput)
                m_hWriteInput = 0
            End If
            Call WaitForSingleObject(m_hProcess, INFINITE)
            Call GetExitCodeProcess(m_hProcess, GetExitCode)
        End If
        Call CloseHandle(m_hProcess)
        m_hProcess = 0
    End If
End Function

Public Function KillProcess() As Boolean
    If Not AtEndOfOutput And Not AtEndOfError Then
        If TerminateProcess(m_hProcess, 0) <> 0 Then
            KillProcess = True
        End If
    End If
End Function

Private Sub Class_Terminate()
    If m_hProcess <> 0 Then
        KillProcess
        Call CloseHandle(m_hProcess)
        m_hProcess = 0
    End If
    If m_hReadOutput <> 0 Then
        Call CloseHandle(m_hReadOutput)
        m_hReadOutput = 0
    End If
    If m_hReadError <> 0 Then
        Call CloseHandle(m_hReadError)
        m_hReadError = 0
    End If
    If m_hWriteInput <> 0 Then
        Call CloseHandle(m_hWriteInput)
        m_hWriteInput = 0
    End If
End Sub
选项显式
DefObj A-Z
私有常量模块\u名称为String=“cExec”
'=========================================================================
"空气污染指数
'=========================================================================
'---用于CreateProcess
Private Const STARTF_USESHOWWINDOW的长度=1
私人Const STARTF_USESTDHANDLES的长度=&H100
私有常量SW_隐藏长度=0
私有常量SW_最小化为长=6
Private Const NORMAL_PRIORITY_类的长度=&H20&
'---用于WaitForSingleObject
私有常量无限长=&hffffff
“---用于重复句柄
Private Const DUPLICATE\u与Long=&H2访问相同
'---用于GetExitCodeProcess
私有常量状态_挂起,长度=&H103
私有声明函数CreateProcessA Lib“kernel32”(ByVal lpApplicationName作为字符串,ByVal lpCommandLine作为字符串,ByVal lpProcessAttributes作为长,ByVal lpThreadAttributes作为长,ByVal bInheritHandles作为长,ByVal dwCreationFlags作为长,ByVal lpEnvironment作为长,ByVal lpCurrentDirectory作为字符串,lpStartupInfo作为STARTUPINFO,lpProcessInformation作为进程信息)作为长
私有声明函数WaitForSingleObject库“kernel32”(ByVal hHandle尽可能长,ByVal DWM尽可能长)尽可能长
私有声明函数getExitCode进程库“kernel32”(ByVal hProcess As Long,lpExitCode As Long)为Long
私有声明函数CloseHandle Lib“kernel32”(ByVal hObject As Long)为Long
私有声明函数CreatePipe Lib“kernel32”(phReadPipe为Long,phWritePipe为Long,lpPipeAttributes为SECURITY_属性,ByVal nSize为Long)为Long
私有声明函数GetCurrentProcess Lib“kernel32”(长度为
私有声明函数DuplicateHandle Lib“kernel32”(ByVal hSourceProcessHandle为Long,ByVal hSourceHandle为Long,ByVal hTargetProcessHandle为Long,lpTargetHandle为Long,ByVal dwDesiredAccess为Long,ByVal bInheritHandle为Long,ByVal dwOptions为Long)为Long
私有声明函数ReadFile Lib“kernel32”(ByVal hFile尽可能长,lpBuffer尽可能长,ByVal nNumberOfBytesToRead尽可能长,lpNumberOfBytesRead尽可能长,ByVal lpOverlapped尽可能长)尽可能长
私有声明函数TerminateProcess Lib“kernel32”(ByVal hProcess As Long,ByVal uExitCode As Long)作为Long
私有声明函数WriteFile Lib“kernel32”(ByVal hFile尽可能长,lpBuffer尽可能长,ByVal nnumberofbytes尽可能长,lpnumberofbytes尽可能长,ByVal lpOverlapped尽可能长)尽可能长
私有声明函数FlushFileBuffers Lib“kernel32”(ByVal hFile作为Long)作为Long
私有声明函数PeekNamedPipe Lib“kernel32”(ByVal hNamedPipe尽可能长,lpBuffer尽可能长,ByVal nBufferSize尽可能长,lpBytesRead尽可能长,lpTotalBytesAvail尽可能长,lpBytesLeftThisMessage尽可能长)尽可能长
私有类型STARTUPINFO
只要
保留为字符串
lpDesktop作为字符串
lpTitle作为字符串
dwX尽可能长
只要
dwXSize尽可能长
尽可能长
dwXCountChars的长度为
德怀康查斯一样长
dwFillAttribute尽可能长
把旗子拖得一样长
wShowWindow作为整数
cbReserved2为整数
lpReserved2尽可能长
HST输入长度为
hst输出长度
hStdError尽可能长
端型
私有类型进程信息
hProcess尽可能长
hThread尽可能长
dwProcessId尽可能长
dwThreadId尽可能长
端型
私有类型安全属性
长度等于
lpSecurityDescriptor的长度
长柄
端型
'=========================================================================
'常量和成员变量
'=========================================================================
私有m_hProcess尽可能长
私有m_线程输出长度为
私有m_线程错误,长度为
专用m_硬件输入长度
'=========================================================================
'错误处理
'=========================================================================
私有子打印错误(sFunc作为字符串)
MsgBox错误&“在”&MODULE_NAME&“&sFunc&”(&Erl&“)中”,vbCritical,App.EXEName
端接头
'=========================================================================
"方法",
'=========================================================================
公众活动(_
sFile作为字符串_
斯巴拉姆斯作为弦_
可选的ByVal bStartHidden作为布尔值_
可选的ByVal bStartMimized As Boolean)作为Boolean
常量FUNC_名称为String=“Run”
作为过程信息的信息
作为STARTUPINFO的Dim uStart
作为字符串的Dim sCommandLine
作为安全属性的Dim uSA
将hTmp变暗为长
将输出变长
长时的模糊错误
将输入变长
关于错误转到EH
“---上一篇
如果m_h进程为0,则
调用CloseHandle(m_hProcess)
m_hProcess=0
如果结束
如果m_线程输出为0,则
调用CloseHandle(m_线程输出)
m_线程输出=0
如果结束
如果m_线程错误为0,则
调用CloseHandle(m_线程错误)