Ok - first off just the code (with a few comments thrown in for luck...) I should note that this code suffers from similar shortcomings to the wshSell solution proposed much earlier in this thread
Create a form with a textbox and a command button on it.
Drop in following code:
[tt]
Option Explicit
Private Sub Command1_Click()
mvarConsoleAllocated = False ' Global flag indicating whether VB has a console allocated or not
InheritableConsole "c:\consoleiotest.bat" ' Give VB a console, and launch the named process
Sleep 500 ' Give new console a chance to respond
Text1.Text = ReadIOConsole ' read any output from console
WriteIOConsole "" ' Effectively just presses a key
Sleep 500 ' Give console a chance to respond
Text1.Text = Text1.Text + ReadIOConsole ' get any additional output from console
CloseConsole ' Free the VB console (which will also close it)
End Sub
Private Sub Form_QueryUnload(Cancel As Integer, UnloadMode As Integer)
If mvarConsoleAllocated = True Then CloseConsole
End Sub
[/tt]
Create a module and drop in the following:
[tt]
Option Explicit
' Console allocation and deallocation
Public Declare Function AllocConsole Lib "kernel32" () As Long
Public Declare Function FreeConsole Lib "kernel32" () As Long
Public Declare Sub Sleep Lib "kernel32" (ByVal dwMilliseconds As Long)
' Declarations for standard handle functions
Private Declare Function SetStdHandle Lib "kernel32" (ByVal nStdHandle As Long, ByVal nHandle As Long) As Long
Private Declare Function GetStdHandle Lib "kernel32" (ByVal nStdHandle As Long) 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 GetCurrentProcess Lib "kernel32" () As Long
' Declarations for standard streams
Private Const STD_ERROR_HANDLE = -12&
Private Const STD_INPUT_HANDLE = -10&
Private Const STD_OUTPUT_HANDLE = -11&
Private Const DUPLICATE_SAME_ACCESS = &H2
' Declarations for all our pipe handles
Private OldStdOut As Long
Private OldStdIn As Long
Private OldStdErr As Long
Private hInReadPipe As Long
Private hInWritePipe As Long
Private hInWritePipeDup As Long
Private hOutReadPipe As Long
Private hOutWritePipe As Long
Private hOutReadPipeDup As Long
Private hErrorReadPipe As Long
Private hErrorWritePipe As Long
Private hErrorReadPipeDup 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 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 CloseHandle Lib "kernel32" (ByVal hObject As Long) As Long
Private Declare Function WriteFile Lib "kernel32" (ByVal hFile As Long, ByVal lpBuffer As String, ByVal nNumberOfBytesToWrite As Long, lpNumberOfBytesWritten As Long, ByVal lpOverlapped As Long) As Long
Public Declare Function ReadConsole Lib "kernel32" Alias "ReadConsoleA" (ByVal hConsoleInput As Long, lpBuffer As Any, ByVal nNumberOfCharsToRead As Long, lpNumberOfCharsRead As Long, ByVal lpReserved As Long) As Long
Public Type OVERLAPPED
Internal As Long
InternalHigh As Long
offset As Long
OffsetHigh As Long
hEvent As Long
End Type
Private Type SECURITY_ATTRIBUTES
nLength As Long
lpSecurityDescriptor As Long
bInheritHandle As Long
End Type
Public mvarConsoleAllocated As Boolean
Public Sub InheritableConsole(strCommand As String)
Dim result As Long
Dim OL As OVERLAPPED
Dim SA As SECURITY_ATTRIBUTES
SA.nLength = Len(SA)
SA.bInheritHandle = True
SA.lpSecurityDescriptor = 0&
OL.offset = 0
OL.OffsetHigh = 0
OL.hEvent = vbNull
AllocConsole ' gives VB STDIN, STDOUT and STDERR
mvarConsoleAllocated = True
' Create three anonomous pipes
result = CreatePipe(hInReadPipe, hInWritePipe, SA, 0&)
result = CreatePipe(hOutReadPipe, hOutWritePipe, SA, 0&)
result = CreatePipe(hErrorReadPipe, hErrorWritePipe, SA, 0&)
' retain just in case (we don't actually use these in this example)
OldStdIn = GetStdHandle(STD_INPUT_HANDLE)
OldStdOut = GetStdHandle(STD_OUTPUT_HANDLE)
OldStdErr = GetStdHandle(STD_ERROR_HANDLE)
' Create uninheritable duplicates of one end of our three pipes
result = DuplicateHandle(GetCurrentProcess, hInWritePipe, GetCurrentProcess, hInWritePipeDup, 0&, False, DUPLICATE_SAME_ACCESS)
CloseHandle hInWritePipe
result = DuplicateHandle(GetCurrentProcess, hOutReadPipe, GetCurrentProcess, hOutReadPipeDup, 0&, False, DUPLICATE_SAME_ACCESS)
CloseHandle hOutReadPipe
result = DuplicateHandle(GetCurrentProcess, hErrorReadPipe, GetCurrentProcess, hErrorReadPipeDup, 0&, False, DUPLICATE_SAME_ACCESS)
CloseHandle hErrorReadPipe
' Ok, now patch in our redirections to standard streams
result = SetStdHandle(STD_OUTPUT_HANDLE, hOutWritePipe)
result = SetStdHandle(STD_INPUT_HANDLE, hInReadPipe)
result = SetStdHandle(STD_ERROR_HANDLE, hErrorWritePipe)
' Launch our console app, using our defined pipes for STDIN, STDOUT and STDERR
StartProcess strCommand, hInReadPipe, hOutWritePipe, hErrorWritePipe
End Sub
Public Sub WriteIOConsole(strString As String)
Dim result As Long
Dim ConsoleOutBuffer As String
Dim cbRead As Long
ConsoleOutBuffer = strString + vbCrLf
cbRead = Len(ConsoleOutBuffer)
result = WriteFile(hInWritePipeDup, ByVal ConsoleOutBuffer, cbRead, cbRead, 0&) ' Write to child's StdIn
End Sub
Public Sub CloseConsole()
FreeConsole
End Sub
Public Function ReadIOConsole() As String
Dim result As Long
Dim ConsoleOutBuffer As String
Dim cbRead As Long
Dim cbBytesToRead As Long
Dim OL As OVERLAPPED
OL.offset = 0
OL.OffsetHigh = 0
OL.hEvent = vbNull
ConsoleOutBuffer = Space(1024)
cbRead = Len(ConsoleOutBuffer)
result = ReadFile(hOutReadPipeDup, ByVal ConsoleOutBuffer, Len(ConsoleOutBuffer), cbRead, 0&) ' read contents of child's StdOut
If result Then
ReadIOConsole = Left(ConsoleOutBuffer, cbRead)
End If
End Function
' Creates a temporary test batch file to interact with (has output and input)
Public Sub MakeBatchFile()
Dim hFile As Long
hFile = FreeFile
Open "c:\consoleiotest.bat" For Output As hFile
Print #hFile, "@echo off"
Print #hFile, "echo This is a demonstration of VB interaction with a console"
Print #hFile, "pause"
Print #hFile, "echo If you can read this line, then VB successfully interacted with a console app"
Print #hFile, "echo All done!"
Close #hFile
End Sub
' get rid of out temporary batch file
Public Sub KillBatchFile()
Kill "c:\consoleiotest.bat"
End Sub
[/color blue][/tt]
Add one additional module (just for a helper function) and drop in the following:
[tt]
Option Explicit
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
Public Type PROCESS_INFORMATION_EXT
hProcess As Long
hThread As Long
hwnd As Long
dwProcessID As Long
dwThreadID As Long
End Type
Private Const NORMAL_PRIORITY_CLASS = &H20
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 Const INFINITE = &HFFFF
Private Declare Function WaitForInputIdle Lib "user32" (ByVal hProcess As Long, ByVal dwMilliseconds As Long) As Long
Public Function StartProcess(strProgram As String, hStdIn As Long, hStdOut As Long, hStdErr As Long) As Long 'PROCESS_INFORMATION_EXT
Dim piProcess As PROCESS_INFORMATION
Dim siStartUp As STARTUPINFO
Dim lResult As Long
siStartUp.hStdInput = hStdIn
siStartUp.hStdOutput = hStdOut
siStartUp.hStdError = hStdErr
lResult = CreateProcessA(vbNullString, strProgram, 0&, 0&, 1&, NORMAL_PRIORITY_CLASS, 0&, vbNullString, siStartUp, piProcess)
WaitForInputIdle piProcess.hProcess, INFINITE 'Let it initialise properly before continuing
StartProcess = lResult
End Function
[/tt]
Right, any questions?