[Home]
[Edit this page]
[Recent Changes]
[Special Pages]
[Help]
CAssert » CSharp-And-ASP-DotNet » CppOperator » Brainfuck » TurboCpp2006 » QbasicFAQ_CallAbs » CP FAQ » talk:Cpp » CppCompiler » server » VBAPIPipes
When we start an application, there are 3 places we can attach pipes:-
Put the code in an event handler for a command button, for example. Run it and you should see (a little time after clicking the button) a message box containing ping results.
It's easy enough to see what the first 3 parameters are - the last two are more confusing. They are in fact related to other things the WriteFile API can do and are used in asynchronus I/O stuff, which is kinda scary and makes me want to run away.
[Edit this page] [Page history] [What links here] [Discuss this topic] [Printer Friendly]
CAssert » CSharp-And-ASP-DotNet » CppOperator » Brainfuck » TurboCpp2006 » QbasicFAQ_CallAbs » CP FAQ » talk:Cpp » CppCompiler » server » VBAPIPipes
Pipes And VB
After a blaze of questions that had a solution involving doing stuff with pipes on the VB board, I decided the best thing to do was to put a page in the CodePedia about it, then point people at that. So, here's the (currently under construction - and feel free to contribute) guide to doing stuff with pipes in VB.What Is A Pipe Anyway
A pipe is a way of passing data from one process to another. Data is put in one end of the pipe by one application and is read from the other end by another application. By setting up a couple of pipes, bi-directional communication is possible. In Windows, we can use the CreatePipe API. This returns two handles, one for each end of the pipe. One will be the read end, and can be used with the ReadFile API. The other will be the write end, and can be used with the WriteFile API. Typically, we give one of the ends to the application when we spawn it, using the CreateProcess API.When we start an application, there are 3 places we can attach pipes:-
- Standard Input is where we attach a pipe if we want to send data to an application. It will expect to read data from the standard input pipe, so you keep the write end.
- Standard Output is where an application will write its normal output, so we attach the write end of a pipe to this and keep hold of the read end.
- Standard Error is where an application will write error messages, so we again attach the write end of a pipe to this and keep hold of the read end.
Relevant APIs
Here I include all the Win32 API function declarations, types and constants that will be used in the examples in this article. They won't all be needed by every example, so you are free to strip out any you are not using. These you just need to copy and paste into an appropriate module. They will be explained in more detail in the examples. Also be aware that I'm working an XP, so I don't know how true some of this will be for Win9x OSes.' These API declarations are for pipe communication stuff.
Public Declare Function CreatePipe Lib "kernel32" ( _
phReadPipe As Long, _
phWritePipe As Long, _
lpPipeAttributes As Any, _
ByVal nSize As Long) As Long
Public 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
Public Declare Function WriteFile Lib "kernel32" ( _
ByVal hFile As Long, _
lpBuffer As Any, _
ByVal nNumberOfBytesToWrite As Long, _
lpNumberOfBytesWritten As Long, _
ByVal lpOverlapped As Any) As Long
Public 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
Public Declare Function CreateProcessA Lib "kernel32" (ByVal _
lpApplicationName As Long, 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
Public Declare Function CloseHandle Lib "kernel32" ( _
ByVal hObject As Long) As Long
' Types related to pipe communication.
Public Type SECURITY_ATTRIBUTES
nLength As Long
lpSecurityDescriptor As Long
bInheritHandle As Long
End Type
Public 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
Public Type PROCESS_INFORMATION
hProcess As Long
hThread As Long
dwProcessId As Long
dwThreadID As Long
End Type
' Some constants that we need for the pipe communication.
Public Const NORMAL_PRIORITY_CLASS = &H20&
Public Const STARTF_USESTDHANDLES = &H100&
Public Const STARTF_USESHOWWINDOW = &H1
Public Const SW_HIDE = 0A First Example
As a starting point, let's consider the case where we want to spawn the ping program and capture its output. We will create a pipe and attach the write end of it to both Standard Out and Standard Error. Here's how we do it.' Here we declare some variables.
Dim retVal As Long
Dim OurReadPipe As Long
Dim AppWritePipe As Long
Dim sa As SECURITY_ATTRIBUTES
Dim startInfo As STARTUPINFO
Dim procInfo As PROCESS_INFORMATION
' These next two lines are important. By default, any pipes that we
' create and have handles to are *not* inherited by any program that
' we spawn. If we don't say that it is OK for them to be inherited,
' they won't have any pipes attached to them. Here we set up a security
' attributes type with the InheritHandle flag set to true.
sa.bInheritHandle = True
sa.nLength = Len(sa)
' Here we call the CreatePipe API to create a pipe for us to use. The
' first parameter is a Long variable that will contain the handle to
' the read end of the pipe. The second is for the write end. The third
' is the security attributes we set up above. The forth is related to
' the number of bytes of data we can have in the pipe at once - 0 lets
' the system use whatever is its default site.
retVal = CreatePipe(OurReadPipe, AppWritePipe, sa, 0)
If retVal = 0 Then MsgBox "Could not create pipe!"
' The next thing we need to do is set up the startup info structure
' for the app we are spawning. First we set some stuff relating to
' handles being passed on, the spawned program being hidden (as it
' is a console app and we don't want a DOS window to pop up) and the
' size of the structure.
startInfo.cb = Len(startInfo)
startInfo.dwFlags = STARTF_USESTDHANDLES Or STARTF_USESHOWWINDOW
startInfo.wShowWindow = SW_HIDE
' Next we set the standard output and standard error pipes to the write
' handles for the pipe we created earlier.
startInfo.hStdOutput = AppWritePipe
startInfo.hStdError = AppWritePipe
' Now we just create the proccess. The second parameter is the program
' we want to spawn and its arguments. You'll notice we pass the security
' and startup info we set up earlier. If you want to know what the other
' parameters are, go look at the MSDN.
retVal = CreateProcessA(0&, "ping www.programmersheaven.com", _
sa, sa, True, NORMAL_PRIORITY_CLASS, _
0&, 0&, startInfo, procInfo)
If retVal = 0 Then MsgBox "Could not start the ping program!"
' Now we have passed the write end of the pipe on, we don't need it, so
' we will close it from our app. Note that the spawned app still has it,
' so the pipe still exists.
CloseHandle AppWritePipe
' The next thing we need to do is read data from the pipe. When we pass
' a string to the Win32 API and expect data to be written into it we MUST
' use a fixed length string. Otherwise, your app will probably segfault, or
' certainly memory will be scribbled on that you'd rather was left alone.
Dim tmpRead As String * 256
Dim readData As String
' We sit in a loop reading from the pipe while we can. When the pipe gets
' broken, the call to ReadFile will fail and retVal will contain 0.
' bytesRead contains how many bytes of data we read from the pipe, so
' we do not end up with null values on the end (or in the middle of!)
' our result string.
Dim bytesRead As Long
Do
retVal = ReadFile(OurReadPipe, tmpRead, 256, bytesRead, 0&)
readData = readData & Left(tmpRead, bytesRead)
DoEvents ' Important if you want your app to stay responsive.
Loop While retVal > 0
' Finally, we're done. We'll print the output string to the debug
' window for now - do what you want with it.
MsgBox readData
Put the code in an event handler for a command button, for example. Run it and you should see (a little time after clicking the button) a message box containing ping results.
Writing To A Pipe
Writing to a pipe is achieved using the WriteFile API. It is easier than reading in some senses, as we know when data will be available and how much of it there is. In fact, if OurWritePipe is a write handle to a pipe and the app we want to send data to holds the read end the following code would send the contents of StuffToWrite down the pipe:-' Declare some variables. Dim ba() As Byte Dim tmp As Long Dim retVal As Long ' Convert string into a byte array. ba = StrConv(StuffToWrite, vbFromUnicode) ' Send it. retVal = WriteFile(OurWritePipe, ba(0), Len(StuffToWrite), tmp, CLng(0))
It's easy enough to see what the first 3 parameters are - the last two are more confusing. They are in fact related to other things the WriteFile API can do and are used in asynchronus I/O stuff, which is kinda scary and makes me want to run away.
Command Line In A Text Box - Example Of Bi-Directional Pipe Communication
In this example we will spawn cmd.exe, the command line interpreter, and use a textbox to allow the user to interact with the command line. Coming soon.Non-blocking Pipe Reads
In the first example we saw how to wait for program termination by continually trying to read. This works becuase calls to ReadFile block - e.g. they wait until there is some data to read or the pipe breaks. That's fine for some situations, but in others you want to see if there is anything to read, and if there isn't go off and do something else. For this we need to use the PeekNamedPipe API to see if there is any data to read. Example coming soon.[Edit this page] [Page history] [What links here] [Discuss this topic] [Printer Friendly]
