Accessing the Standard Input and Output
Streams
While it is necessary to turn to the Windows
API to access stdin and stdout, it is nonetheless very easy. You
must first get a handle to stdin or stdout with the function GetStdHandle.
Its declaration is as follows:
Public Declare Function
GetStdHandle Lib "kernel32" _
(ByVal nStdHandle As Long) As Long
The argument nStdHandle
specifies which handle you want; pass either STD_OUTPUT_HANDLE
(its value being 11&) or STD_INPUT_HANDLE
(its value 10&) as needed.
To read from stdin, you use the API function ReadFile
to get the data. The declaration of this function is:
Private Declare Function ReadFile
Lib "kernel32" _
(ByVal hFile As Long, _
ByVal lpBuffer As Any, _
ByVal nNumberOfBytesToRead As Long, _
lpNumberOfBytesRead As Long, _
ByVal lpOverlapped As Any) As Long
Here, the parameters are as follows:
- hFile is the stdin handle
obtained from GetStdHandle.
- lpbuffer is a string
variable where the data will be placed.
- nNumberOfBytesToRead is
the number of bytes to read. Its value should be one less
that the size of lpbuffer.
- lpNumberOfBytesRead is a
type Long where the function returns the
actual number of bytes read.
- lpOverlapped is not used
when reading from stdin; pass a value of zero.
To send data to stdout, use the API function WriteFile.
Its declaration is:
Private Declare Function WriteFile
Lib "kernel32" _
(ByVal hFile As Long, _
ByVal lpBuffer As Any, _
ByVal nNumberOfBytesToWrite As Long, _
lpNumberOfBytesWritten As Long, _
ByVal lpOverlapped As Any) As Long
The parameters are defined as follows:
- hFile is the stdout
handle obtains from GetStdHandle.
- lpBuffer is a string
variable containing the data to be written.
- nNumberOfBytesToWrite is
the number of bytes to write (the length of lpBuffer).
- lpNumberOfBytesWritten is
where the function returns the actual number of bytes
written.
- lpOverlapped is not used
when writing to stdout; pass a value of 0.
Here is some code that shows you how to use
these functions to access stdin and stdout:
Dim inbuf As String, lBytesRead As
Long, hStdIn As Long
Dim outbuf As String, hStdout As Long, lBytesWritten As Long
inbuf = Space(2000)
hStdIn = GetStdHandle(STD_INPUT_HANDLE)
ReadFile hStdIn, inbuf, Len(inbuf) - 1, lBytesRead, 0&
' Now inbuf contains data from stdin.
hStdout = GetStdHandle(STD_OUTPUT_HANDLE)
' Write data in outbuf to stdout.
Call WriteFile(hStdout, (outbuf), Len(outbuf), lBytesWritten,
0&)
Using Stdin
The supplemental data that is sent as part of a
POST request is placed in stdin by the server.
As mentioned earlier, the stdin stream is not used with GET
requests when the user data is obtained in the QUERY_STRING
environment variable. When POST is used, the CONTENT_TYPE
and CONTENT_LENGTH environment variables contain
information about the size and format of the data available
through stdin. The data in stdin is formatted as follows:
- Spaces are replaced by plus signs.
- Names and values are separated by equal
signs.
- Entries are separated by ampersands.
Heres an example of user data returned in
stdin:
keywords=monica+clinton&sort=ascending
This means that the user submission element
named keywords has the value monica clinton and the
element named sort has the value ascending. After
reading the data from stdin, code in your program will have to
separate out the individual values so they can be used.
Creating the Response
Once your CGI program retrieves the data
submitted by the user, it needs to perform whatever processing is
required. This may be database access or some other taskthat
depends on the purpose you wrote the program for. In any case,
the final act of the CGI program will be to write its response to
stdout.
What must be written to stdout? You must write
the entire HTML page that the user will eventually see, including
all HTML tags as well as content. In addition, the response must
include some HTTP response headers that are required by the
server. The headers you include determine whether you create a
direct response or an indirect response.
A direct response is sent by the server to the
user with no processing. This means that only the HTTP headers
you included will be sent. To create a direct response, begin the
response with a status header, which is a sequence of three text
variables with the following structure:
Protocol/Version Status Reason
Protocol/version
will be HTTP/1.0. Status is a three
digit numerical code describing the results of the request, and Reason
is a brief text description of the result. For example, a status
header would look like this:
HTTP/1.0 200 OK
This status header indicates that the request
completed successfully. Other codes and descriptions are used for
various error conditions. Following the first line of a direct
HTTP response, you can optionally include additional headers that
provide information to the client. You can find details on other
status codes and HTTP headers in any book that covers the HTTP
protocol in depth, as well as some advanced HTML texts. As an
example, here is a set of direct response HTTP headers:
HTTP/1.0 200 OK
Server: Microsoft-IIS/4.0
Date: Friday, 9-OCT-1998 10:15:00 GMT
Last-modified: Thursday, 8-OCT-1998 21:35:30 GMT
Content-type: text/html
Creating an Indirect Response
You can also send an indirect response by
beginning the data with the content-type header. Since your
response will be an HTML document, the type is text/html and the
complete header will be as follows:
Content-type: text/html
When you send an indirect response, the server
recognizes it as such and will add additional headers to create a
completely formed HTTP response before sending it to the client.
What comes after the headers? A blank line and
then your HTML response. Here is an example of a simple but
complete response that a CGI program could send back to the users
Web browser:
Content-type: text/html
<html>
<head>
<title>Sample CGI output</title>
</head>
<body>
Hello, world!
</body>
</html>
A Demonstration
Enough talk, lets code! I have written a
very simple CGI application. It consists of an HTML page that
lets the user input his or her name and then submits it to
SIMPLECGI.EXE, written in Visual Basic. The CGI program retrieves
the user information and responds with a page that greets the
user by name and displays some other information that is
available in environment variables. Listing 1 shows the HTML
documents text, and Listing 2 contains the Basic listing
for SIMPLECGI.BAS. By the way, be sure to use the same
declarations of WriteFile() and ReadFile()
that I use here. If you try to use the declarations of these
functions returned by the Visual Basic API Text Viewer, be aware
that these include errors and your program will not work.
You can run this program under the Microsoft
Personal Web Server or most any other Web server. Create an EXE
and place it in your CGI-BIN directory. If your CGI executable
directory is something else be sure to edit the FORM tag in
SIMPLECGI.HTM to point to the proper location. Place the HTML
document in the root folder and navigate to it. As with all
Visual Basic programs, the Visual Basic runtime must be installed
on the server for the program to run.
The Special Nature of CGI Apps in
Visual Basic
CGI applications in Visual Basic are unlike any
Visual Basic application you have probably ever created. For one
thing, they have no visual interfaceno forms or controls,
just code. Your Visual Basic project will contain only a code
module, which must contain a procedure named Main()
where execution will begin. Because there is no one to respond to
a CGI application, it cannot display message boxes and in fact
cannot be interactive in any way; it needs to simply run on its
own.
The no-user-interaction consideration is
relevant to error trapping as well. You must make sure that error
trapping is enabled throughout the program so that the default
error message box will never be displayed. You can write error
messages to the output so the user will see them. You can also
write error messages to a log file on the server.
If you need to create CGI applications, Visual
Basic can be very useful. One of the reasons that PERL is so
popular for CGI programming is its robust text handling
capabilities, and we all know that it is hard to beat Visual
Basic when it comes to text! v
During daylight hours, Peter is a research
scientist at Duke University Medical Center in Durham, North
Carolina. Comments and suggestions may be sent to him at paitken@acpub.duke.edu
Copyright © 1999 The Coriolis Group, LLC. All rights reserved.