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
Eline sağlık dostum, bende de bu iş için bir kod vardı .... bulursam onu da paylaşayım, alternatif olsun.
YanıtlaSil