Reading Console Window StdOut With Real Time in VBA

    Yaklaşık 8 yıl önce kaynağını nereden aldığımı hatırlayamadığım kodu paylaşmak istiyorum. Bu kod, DOS ekranındaki çıktıları gerçek/eş zamanlı olarak okuyabilmenizi sağlar. DOS ekranından alınabilecek çıktılar için DIR veya PING komutları gibi sığ düşünmeyin. Bu, herhangi bir Console uygulaması olabilir. Örneğin 7zip veya bir Sql Server veritabanının yedekleme için kullanılan Console uygulaması olabilir.

    Kaynak kod üzerinde biraz oynayarak nesneye çevirdim. Daha önemlisi hem 32, hem de 64 bit Office için Win32 API deklarasyonlarını uyumlu hale getirdim. Örnek kullanım ve kaynak kod aşağıdadır. Ayrıca yazının sonunda örnek dosyayı da indirebileceğiniz bir bağlantı bulacaksınız.

 Örnek dosyadan bir görüntü:

 

 Nesnenin kullanım örneği (UserForm):

 

Private WithEvents cReader As ConsoleReader

Private Sub CommandButton1_Click()
    'Just Commanline example.
    'ApplicationName parameter is null in CreateProcess API
    
    CommandButton1.Enabled = False
    CommandButton1.Caption = "Executing..."
    
    txtLog.SetFocus
    
    txtLog.Text = ""
    
    Dim batchReturn As String 'this is in comment in source code of the GetConsole function
    
    Set cReader = New ConsoleReader
    
    batchReturn = cReader.GetConsole("", txtJustCommandline.Text)
    
    CommandButton1.Caption = "Execute"
    CommandButton1.Enabled = True
End Sub

Private Sub CommandButton2_Click()
    'With Application Name example
    'ApplicationName parameter isn't null in CreateProcess API(Path & Name required)
    
    CommandButton2.Enabled = False
    CommandButton2.Caption = "Executing..."
    
    txtLog.SetFocus
    
    txtLog.Text = ""
    
    Dim batchReturn As String 'this is in comment in source code of the GetConsole function
    
    Set cReader = New ConsoleReader
    
    batchReturn = cReader.GetConsole("C:\Windows\System32\cmd.exe", txtWithAppName.Text)
    
    CommandButton2.Caption = "Execute"
    CommandButton2.Enabled = True
End Sub

Private Sub cReader_ConsoleMessage(ByVal message As String)
    DoEvents
    txtLog.Text = txtLog.Text & message
End Sub

 

 ConsoleReader.cls:

 

Option Explicit

#If Win64 Then
    Private Declare PtrSafe Function CreatePipe Lib "kernel32" (phReadPipe As LongLong, phWritePipe As LongLong, _
        lpPipeAttributes As Any, ByVal nSize As Long) As Long
                                                                
    Private Declare PtrSafe Function ReadFile Lib "kernel32" (ByVal hFile As LongLong, ByVal lpBuffer As String, _
        ByVal nNumberOfBytesToRead As Long, lpNumberOfBytesRead As Long, ByVal lpOverlapped As Any) As Long
        
    Private Declare PtrSafe Function CreateProcess Lib "kernel32" Alias "CreateProcessA" (ByVal lpApplicationName As Any, _
        ByVal lpCommandLine As String, lpProcessAttributes As Any, lpThreadAttributes As Any, ByVal bInheritHandles As Long, _
        ByVal dwCreationFlags As Long, ByVal lpEnvironment As Long, ByVal lpCurrentDirectory As Long, lpStartupInfo As Any, _
        lpProcessInformation As Any) As Long
    
    Private Declare PtrSafe Function CloseHandle Lib "kernel32" (ByVal hObject As LongLong) As Long
    
    Private Type SECURITY_ATTRIBUTES
        nLength As Long
        lpSecurityDescriptor As LongLong
        bInheritHandle As Long
    End Type
    
    Private Type STARTUPINFO
        cb As Long
        lpReserved As LongLong
        lpDesktop As LongLong
        lpTitle As LongLong
        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 LongLong
        hStdInput As LongLong
        hStdOutput As LongLong
        hStdError As LongLong
    End Type
    
    Private Type PROCESS_INFORMATION
        hProcess As LongLong
        hThread As LongLong
        dwProcessId As Long
        dwThreadID As Long
    End Type
    
#Else

    Private Declare Function CreatePipe Lib "kernel32" (phReadPipe As Long, phWritePipe As Long, _
        lpPipeAttributes As Any, ByVal nSize As Long) As Long
                                                                
    Private Declare Function ReadFile Lib "kernel32" (ByVal hFile As Long, ByVal lpBuffer As String, _
        ByVal nNumberOfBytesToRead As Long, lpNumberOfBytesRead As Long, ByVal lpOverlapped As Any) As Long
        
    Private Declare Function CreateProcess Lib "kernel32" Alias "CreateProcessA" (ByVal lpApplicationName As Any, _
        ByVal lpCommandLine As String, lpProcessAttributes As Any, lpThreadAttributes As Any, ByVal bInheritHandles As Long, _
        ByVal dwCreationFlags As Long, ByVal lpEnvironment As Long, ByVal lpCurrentDirectory As Long, lpStartupInfo As Any, _
        lpProcessInformation As Any) As Long
    
    Private Declare Function CloseHandle Lib "kernel32" (ByVal hObject As Long) As Long
    
    Private Type SECURITY_ATTRIBUTES
        nLength As Long
        lpSecurityDescriptor As Long
        bInheritHandle As Long
    End Type
    
    Private Type STARTUPINFO
        cb As Long
        lpReserved As Long
        lpDesktop As Long
        lpTitle As Long
        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
    
#End If

Private Const NORMAL_PRIORITY_CLASS = &H20&
Private Const STARTF_USESTDHANDLES = &H100&
Private Const STARTF_USESHOWWINDOW = &H1
Private Const SW_HIDE = 0

Public Event ConsoleMessage(ByVal message As String)

Public Function GetConsole(Optional ByVal appFullPath As String = "", Optional ByVal strCommand As String = "", _
                           Optional ByVal nBytesRead As Long = 1048576) As String  ' 1048576 => 1 MB
                    
    Dim sa           As SECURITY_ATTRIBUTES
    Dim startInfo    As STARTUPINFO
    Dim procInfo     As PROCESS_INFORMATION
    Dim strRet       As String
    Dim bytesRead    As Long
    Dim readBuffer   As String
    Dim retVal       As Long
    
#If Win64 Then
    Dim OurReadPipe  As LongLong
    Dim AppWritePipe As LongLong
#Else
    Dim OurReadPipe  As Long
    Dim AppWritePipe As Long
#End If
    
    sa.bInheritHandle = True
    sa.nLength = Len(sa)
    
    retVal = CreatePipe(OurReadPipe, AppWritePipe, sa, 0)
    
    If retVal = 0 Then
        MsgBox "Could not create pipe!"
        End
    End If
    
    startInfo.cb = Len(startInfo)
    startInfo.dwFlags = STARTF_USESTDHANDLES Or STARTF_USESHOWWINDOW
    startInfo.wShowWindow = SW_HIDE
    
    startInfo.hStdOutput = AppWritePipe
    startInfo.hStdError = AppWritePipe
                                            
    If appFullPath = "" Then
        retVal = CreateProcess(0&, strCommand, sa, sa, True, NORMAL_PRIORITY_CLASS, 0&, 0&, startInfo, procInfo)
    Else
        retVal = CreateProcess(appFullPath, strCommand, sa, sa, True, NORMAL_PRIORITY_CLASS, 0&, 0&, startInfo, procInfo)
    End If
    
    If retVal = 0 Then MsgBox "Could not start the ping program!"
    
    CloseHandle AppWritePipe
    
    readBuffer = Space(nBytesRead * 10)
    
    Do
        retVal = ReadFile(OurReadPipe, readBuffer, nBytesRead, bytesRead, 0&)
        If retVal = 0 Then Exit Do
        
        strRet = Left$(readBuffer, bytesRead)
        
        RaiseEvent ConsoleMessage(strRet)
        
        'Debug.Print strRet
        'RaiseEvent Myvent(Byval msg As String)
        'GetConsole = GetConsole & strRet
    Loop
    
    CloseHandle procInfo.hProcess
    CloseHandle procInfo.dwThreadID
    CloseHandle OurReadPipe
End Function

Private Function GetConsoleTR(txt As String) As String
    ' Zeki Gürsoy
    '
    ' ı ğ ü ş ç ö Ğ Ü İ Ş Ç Ö => cp857 or ibm857 => Turkish DOS
    
    Dim bytes() As Byte, i As Integer
    
    bytes = txt

    For i = 0 To UBound(bytes) Step 2
        Select Case bytes(i)
            Case 141 And bytes(i + 1) = 0 '"ı"
                bytes(i) = 49: bytes(i + 1) = 1
            Case 167 And bytes(i + 1) = 0 '"ğ"
                bytes(i) = 31: bytes(i + 1) = 1
            Case 129 And bytes(i + 1) = 0 '"ü"
                bytes(i) = 252: bytes(i + 1) = 0
            Case 120 And bytes(i + 1) = 1 '"ş"
                bytes(i) = 95: bytes(i + 1) = 1
            Case 33 And bytes(i + 1) = 32 '"ç"
                bytes(i) = 231: bytes(i + 1) = 0
            Case 29 And bytes(i + 1) = 32 '"ö"
                bytes(i) = 246: bytes(i + 1) = 0
            Case 166 And bytes(i + 1) = 1 '"Ğ"
                bytes(i) = 30: bytes(i + 1) = 1
            Case 97 And bytes(i + 1) = 1 '"Ü"
                bytes(i) = 220: bytes(i + 1) = 0
            Case 220 And bytes(i + 1) = 2 '"İ"
                bytes(i) = 48: bytes(i + 1) = 1
            Case 158 And bytes(i + 1) = 0 '"Ş"
                bytes(i) = 94: bytes(i + 1) = 1
            Case 172 And bytes(i + 1) = 32 '"Ç"
                bytes(i) = 199: bytes(i + 1) = 0
            Case 34 And bytes(i + 1) = 33 '"Ö"
                bytes(i) = 214: bytes(i + 1) = 0
        End Select
    Next
    
    GetConsoleTR = bytes
End Function

 

Örnek dosya (30,8 KB) : ReadConsole(32&64).xlsm 

Yorumlar

  1. Eline sağlık dostum, bende de bu iş için bir kod vardı .... bulursam onu da paylaşayım, alternatif olsun.

    YanıtlaSil

Yorum Gönder

Bu blogdaki popüler yayınlar

VBA: Kombine Stok Değerleme Yöntemleri (FIFO, LIFO, Ağırlıklı Ortalama, Hareketli Ortalama)

Firefox Webrowser For VBA

VBA: Common controls Enter/Exit events and fast WinAPI call with Type Library