We help IT Professionals succeed at work.

Check out our new AWS podcast with Certified Expert, Phil Phillips! Listen to "How to Execute a Seamless AWS Migration" on EE or on your favorite podcast platform. Listen Now

x

CreateProcess/Pipe Problem

BrianGEFF719
BrianGEFF719 asked
on
Medium Priority
1,935 Views
Last Modified: 2008-02-20
Here is what I am trying to do. I am trying to create a program that will spawn a DOS Program and then redirect the output to a byte array and then I also want to be able to send input to the DOS Program via a byte buffer. The only way I could think of doing this to keep the process open and allow the user to send the input was to create a Class Module, below is what I have so far.


Private Declare Function CreatePipe Lib "kernel32" (phReadPipe As Long, phWritePipe As Long, lpPipeAttributes As SECURITY_ATTRIBUTES, ByVal nSize As Long) As Long
Private Declare Sub GetStartupInfo Lib "kernel32" Alias "GetStartupInfoA" (lpStartupInfo As STARTUPINFO)
Private Declare Function CreateProcess Lib "kernel32" Alias "CreateProcessA" (ByVal lpApplicationName As String, ByVal lpCommandLine As String, lpProcessAttributes As Any, lpThreadAttributes As Any, ByVal bInheritHandles As Long, ByVal dwCreationFlags As Long, lpEnvironment As Any, ByVal lpCurrentDriectory As String, lpStartupInfo As STARTUPINFO, lpProcessInformation As PROCESS_INFORMATION) As Long
Private Declare Function SetWindowText Lib "user32" Alias "SetWindowTextA" (ByVal hwnd As Long, ByVal lpString As String) As Long
Private Declare Function ReadFile Lib "kernel32" (ByVal hFile As Long, lpBuffer As Any, ByVal nNumberOfBytesToRead As Long, lpNumberOfBytesRead As Long, lpOverlapped As Any) As Long
Private Declare Function SendMessage Lib "user32" Alias "SendMessageA" (ByVal hwnd As Long, ByVal wMsg As Long, ByVal wParam As Long, lParam 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 PROCESS_INFORMATION
 hProcess As Long
 hThread As Long
 dwProcessId As Long
 dwThreadId 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 Byte
 hStdInput As Long
 hStdOutput As Long
 hStdError As Long
End Type
Private Type OVERLAPPED
   ternal As Long
   ternalHigh As Long
   offset As Long
   OffsetHigh As Long
   hEvent As Long
End Type
Private Const STARTF_USESHOWWINDOW = &H1
Private Const STARTF_USESTDHANDLES = &H100
Private Const SW_HIDE = 0
Private Const EM_SETSEL = &HB1
Private Const EM_REPLACESEL = &HC2
Private hStdOut As Long, hStdIn As Long 'pipes for use by the Shell
Private hRead As Long 'Pipes used by us ;/
Private hWrite As Long
Private ProcessAttrib As SECURITY_ATTRIBUTES
Public Function CreateCMD()
Dim startInfo As STARTUPINFO
Dim lngPipe1ret As Long, lngPipe2ret As Long
Dim lngProcessRet As Long
Dim ProcessInfo As PROCESS_INFORMATION
Dim pipeAttrib As SECURITY_ATTRIBUTES
Dim ThreadAttrib As SECURITY_ATTRIBUTES

'Create Two Pipes
pipeAttrib.nLength = Len(pipeAttrib)
pipeAttrib.lpSecurityDescriptor = 0
pipeAttrib.bInheritHandle = True
lngPipe1ret = CreatePipe(hRead, hStdOut, pipeAttrib, 0)     'hStdOut -> hRead
lngPipe2ret = CreatePipe(hStdIn, hWrite, pipeAttrib, 0)       'hWrite -> hStdIn

'make sure pipes were created
If (lngPipe1ret = 0) Or (lngPipe2ret = 0) Then
    CreateCMD = -2  'return -2 and exit
    Exit Function
End If

'Set Startup Info
startInfo.cb = Len(startInfo)
GetStartupInfo startInfo

'Set Pipes
startInfo.hStdOutput = hStdOut
startInfo.hStdError = hStdOut
startInfo.hStdInput = hStdIn

'Set Flags
startInfo.dwFlags = STARTF_USESHOWWINDOW Or STARTF_USESTDHANDLES
startInfo.wShowWindow = SW_HIDE

'Create the process
ProcessAttrib.nLength = Len(ProcessAttrib)
ThreadAttrib.nLength = Len(ThreadAttrib)
lngProcessRet = CreateProcess(vbNullString, "program.exe", ProcessAttrib, ThreadAttrib, True, 0, Null, vbNullString, startInfo, processInfo)
If lngProcessRet > 0 Then
    CreateCMD = ProcessInfo.dwProcessId
Else
    CreateCMD = -1
End If
End Function

Public Function ReadCMD() As Byte()
Dim lngRet As Long
Dim lngBytesRead As Long
Dim byteBuff(1024) As Byte
lngRet = ReadFile(hRead, byteBuff(0), 1023, lngBytesRead, 0&)
End Function




As of now the CreateCMD works, however, when I do a readCMD it reads the data, but right after that my program freezes.




Here is the code I am using in my form:

Private cmdShell As New cmd
Private hasShell As Boolean

Private Sub Form_Load()
Dim retCode As Long
retCode = cmdShell.CreateCMD
If retCode > 0 Then hasShell = True
End Sub

Private Sub Timer1_Timer()
Dim byteBuff(1024) As Byte
If hasShell = True Then
    byteBuff(0) = cmdShell.ReadCMD
   ' Text1.Text = Text1.Text & byteBuff()  <--- dont pay attention to this, just for testing
End If
End Sub



if I disable the timer after the first call of ReadCmd it wont freeze on me, however, I need to be able to let the program to hang out until the user tells it what to do.


-Brian
Comment
Watch Question

Author

Commented:
I think it only freezes when there is nothing to read in the pipe? Any suggests of how to be alerted when there is data in the pipe to be read?
I try your example above.
Program hangs when no data is available from your console application.
To try this change:
Public Function ReadCMD() As Byte()
Dim lngRet As Long
Dim lngBytesRead As Long
Dim byteBuff(1024) As Byte
lngRet = ReadFile(hRead, byteBuff(0), 1023, lngBytesRead, 0&)
End Function

To

Public Function ReadCMD() As Byte()
Dim lngRet As Long
Dim lngBytesRead As Long
Dim byteBuff(1024) As Byte
lngRet = ReadFile(hRead, byteBuff(0), 10, lngBytesRead, 0&) ' Read only 10 bytes from console
 ' When you read about 14 times (140 bytes) there are no more bytes available from console application
 ' Because ReadFile is synchronous function, it will release control to your application when some data is available from your console application.
End Function

I think that ReadFile always will hand your application until data is available.

Try using ReadFileEx with Overlapped structure and some Your defined Function that will be executed from windows when data is available. Then you program will not hang on ReadFile.

Author

Commented:
I am not understanding that, i am going to retrieve the data from the pipe as soon as the process is created. Then the user is going to write data to the write pipe, I need to be able to check when data is ready in the read pipe, because the console program is going to do some processing? Do you understand now?
Yes, understand, but you use ReadFile as synchronous function, and when no data is available your program have no chance to continue it's execution.
Use ReadFileEx:

The ReadFileEx function reads data from a file asynchronously. It is designed solely for asynchronous operation, unlike the ReadFile function, which is designed for both synchronous and asynchronous operation. ReadFileEx lets an application perform other processing during a file read operation.

The ReadFileEx function reports its completion status asynchronously, calling a specified completion routine when reading is completed and the calling thread is in an alertable wait state.


BOOL ReadFileEx(

    HANDLE hFile,      // handle of file to read
    LPVOID lpBuffer,      // address of buffer
    DWORD nNumberOfBytesToRead,      // number of bytes to read
    LPOVERLAPPED lpOverlapped,      // address of offset
    LPOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine       // address of completion routine
   );


lpCompletionRoutine

Points to the completion routine to be called when the read operation is complete and the calling thread is in an alertable wait state. For more information about the completion routine, see FileIOCompletionRoutine.

Author

Commented:
I am a little confused by ReadFileEx, could you possibly provide an example?


Should it be something like this


dim over_lapped as OverLapped
dim boolRet as boolean

boolRet = ReadFileEx(readPipe,byteBuff(0),1023,over_lapped, AddressOf NextFunction)

Author

Commented:
But the question is, why does ReadFile freeze my application when no data is availible?


-Brian
Unlock this solution with a free trial preview.
(No credit card required)
Get Preview
Unlock the solution to this question.
Thanks for using Experts Exchange.

Please provide your email to receive a free trial preview!

*This site is protected by reCAPTCHA and the Google Privacy Policy and Terms of Service apply.

OR

Please enter a first name

Please enter a last name

8+ characters (letters, numbers, and a symbol)

By clicking, you agree to the Terms of Use and Privacy Policy.