Dennis Crain
Microsoft Developer Network Technology Group
November 1995
Click to open or copy the files in the CGISPAWN sample application.
This article describes the CGISPAWN utility, an application that enables common gateway interface (CGI) programs to be written in languages that do not support standard input/output (I/O). The article briefly describes CGISPAWN and is followed by a description of using it in conjunction with HTML forms. The article concludes with some Visual Basic® examples that illustrate the steps required to use CGISPAWN.
The only way for a common gateway interface (CGI) application to return data to the client is by way of writing to standard out (STDOUT). Some programming languages do not support standard input or output. A good example of this is Visual Basic®. Ironically, Visual Basic provides a rich and highly abstracted environment that would make it ideal for writing useful CGI applications. On the other hand, if Visual Basic did provide a means of conveniently reading and writing standard I/O, it would be a very powerful and convenient tool for creating CGI applications. One way to rectify this problem is to have an application that handles all standard I/O issues on behalf of applications written in Visual Basic or any other language that does not support standard I/O. This article describes just such an application, CGISPAWN.
CGISPAWN is a 32-bit Microsoft® Windows NT™ console application that is itself a CGI-compliant application. Its sole purpose in life is to receive data from the HTTP server, call another application (that is, a Visual Basic application) to deal with that data, and then send the results from that application back to the HTTP server. Figure 1 illustrates the relationship between the HTTP server, CGISPAWN, and your application.
Note CGISPAWN is a sample application. Very little error checking is performed in an effort to keep the sample as instructive as possible. Should you want to use CGISPAWN in a commercial or production setting, modify the source code appropriately to accommodate error conditions.
Figure 1. Relationship between HTTP Server, CGISPAWN, and your application
When first called, CGISPAWN attempts to create two temporary files. The names of these files are generated by GetTempFileName, a Win32® Application Programming Interface (API). The path for these files is determined by using the Win32 API GetTempPath. Just before CGISPAWN terminates, it deletes these files. For the purpose of discussion, I will refer to these files as the Infile and the Outfile.
When the client submits form data to the HTTP server, CGISPAWN takes this data and writes it to the Infile. The data written to the Infile is composed of two distinct types. The first type of data is the name/value pairs from the form. The second type of data is the HTTP server environment variables. This data is written under the headings [NameValuePairs] and [Environment], respectively.
CGISPAWN also creates the Outfile. However, CGISPAWN is not responsible for writing data to the Outfile; this is the responsibility of the application that CGISPAWN spawns. Typically, the Outfile contains server directives such as Content-type or Location. Usually, HTML text follows a Content-type directive.
The temporary file names for the Infile and Outfile are passed to the spawned application by way of the command line. There are three arguments in the command line. The first argument is the fully qualified path to CGISPAWN.EXE. The second argument is the name of the Infile. The third argument is the name of the Outfile. Both of these filenames include the path.
I will illustrate use of the Infile and Outfile within a Visual Basic application near the end of this article.
It is conceivable that someone could modify an HTML form so that CGISPAWN would call a program such as format or even a virus that they had somehow copied into your WWW tree (however, why would you permit this?). As a safeguard, CGISPAWN first looks in the registration database for the name of the application to be run. If the registration database does not contain the application name, CGISPAWN exits prior to calling the application. Admittedly, this is probably not the definitive level of security, but remember that this is a sample. You may wish to enhance the security feature should you believe that it is not sufficient for your needs.
For CGISPAWN to work correctly, it must be in a location on the server where it can be found, and it must be referred to properly in any HTML fill-in forms that use it.
You may recall that the HTTP data path may be conveniently specified in the HTTP server by using the HTTP Server application in Control Panel. In version 0.94 of the HTTP server, the application contains a field titled Data directory. Let's assume that the value of this field is C:\WWW. In this case, CGISPAWN must reside in this directory or any directory for which C:\WWW is an ancestor. In Figure 2, CGISPAWN.EXE is located in a directory called "bin," which is a subdirectory of the WWW directory. Make note of the location, because you will refer to it in any forms you create that use CGISPAWN.
Figure 2. Typical location for CGISPAWN.EXE in HTTP data path
As mentioned earlier, CGISPAWN looks in the registration database of the machine on which the HTTP server resides for the name of the application to be spawned. If the name is not found, CGISPAWN terminates. To register your applications, use REGEDT32.EXE (usually found in the Windows NT SYSTEM32 directory).
You will need to create subkeys under the SOFTWARE subkey in HKEY_LOCAL_MACHINE. Create a cgispawn subkey. Then create values within it. In Figure 3, two applications have been added within the cgispawn subkey—VBCGI.EXE and VBJAG.EXE.
Figure 3. REGEDT32 window showing applications that CGISPAWN will spawn
Using CGISPAWN in your forms requires the use of the usual FORM field. It also requires the additional use of one hidden field in the form and the use of an optional second hidden field.
When creating an HTML fill-in form, you must use the HTML FORM element. This is business-as-usual. If you are not familiar with the use of this element, take a look at the reference listed at the end of this article.
The following HTML text illustrates the use of FORM with the attribute ACTION. Note that the universal resource locator (URL) after ACTION is the location of CGISPAWN.EXE (see Figure 2 above). The method in this sample is POST; however, you can use the GET method as well. CGISPAWN supports both methods.
<FORM ACTION="/bin/cgispawn.exe" METHOD=POST>
When you submit the form contents to the server, name/value pairs are transmitted. Among these name/value pairs must be the pair "spawn=foo.exe". CGISPAWN searches for the name "spawn" and saves the value, in this case "foo.exe". The value "foo.exe" is a program that you have written. It may be a Visual Basic application that takes the data provided by the HTML fill-in form and queries a Microsoft Access database. The following HTML text illustrates the method to place this name/value pair in your form. The field does not need to be hidden. Hiding it simply cleans up the form of information the user does not need to see.
<INPUT TYPE="hidden" NAME="spawn" VALUE = "/jaginfo/jaglove/vbjag.exe" SIZE=255>
CGISPAWN also searches the name/value pairs for the name "timeout". If found, the value is used as the timeout for the process created within CGISPAWN to run the application as described above. The value is specified in milliseconds. CGISPAWN creates the process using the CreateProcess Win32 API.
<INPUT TYPE="hidden" NAME="timeout" VALUE = "15000" SIZE=10>
The following HTML fill-in form obtains search criteria from the user.
Figure 4. Form used to obtain search criteria
The following is the source text for this form. If you look carefully, you will see the use of the above-mentioned HTML elements (in bold text).
<HTML><HEAD>
<TITLE>Query Jag Lover's Digest </TITLE>
</HEAD>
<BODY>
<HR><H1> Query Jag Lover's Digest </H1><UL><HR>
<FORM ACTION="http://bones/bin/cgispawn.exe" METHOD=POST>
<INPUT TYPE="hidden" NAME="spawn" VALUE = "/jaginfo/jaglove/vbjag.exe" SIZE=255>
<INPUT TYPE="hidden" NAME="timeout" VALUE = "15000" SIZE=10>
The Jag Lover's Digest is a database of all mail messages sent
to members of the Jag-Lovers alias. If you haven't already guessed,
Jag-Lovers are those who own or are interested in Jaguar, the car.
The database consists of four fields, FROM, DATE, SUBJECT, and
MESSAGE. You may conduct a search on any combination of these
fields by entering data in this form.
<p>
1) <STRONG>From: </STRONG>
<INPUT TYPE="text" NAME="from" SIZE=25>
<p>
2) <STRONG>Date: </STRONG>
<INPUT TYPE="text" NAME="date" SIZE=20>
<p>
3) <STRONG>Subject: </STRONG>
<INPUT TYPE="text" NAME="subject" SIZE=60>
<p>
4) <STRONG>Message: </STRONG>
<INPUT TYPE="text" NAME="message" SIZE=60>
<p>
<p>
<INPUT TYPE="submit" VALUE="Submit Query"><INPUT TYPE="reset"
VALUE="Clear Form">
</FORM>
</BODY>
</HTML>
Note that CGISPAWN will only spawn 32-bit applications. The following discussion of Visual Basic assumes that you are using Visual Basic version 4.0.
Any Visual Basic application that uses CGISPAWN is required to do four things. The application must:
Beyond these requirements, the application may do anything that you desire. The following code snippets illustrate how to do each one.
Visual Basic saves the command-line arguments in a variable called Command. The following code illustrates how to obtain the name of the Infile and Outfile. Note that Visual Basic ignores the first argument (the path to CGISPAWN). In this example, if the command line is not found, the application terminates. The Infile and Outfile file names are stored in the strings Infile$ and Outfile$, respectively.
If Command <> "" Then
nPos = InStr(1, Command, Chr(32), 1)
Infile$ = Left(Command, nPos)
Outfile$ = Right(Command, Len(Command) - nPos)
Else
End
End If
It is not essential to read the contents of the Infile. However, if your application is going to do anything useful, it will need to read the contents.
In the following example, the Infile and Outfile are opened. The error handling is strongly suggested.
On Error GoTo ErrHandler
Open Infile$ For Input As #1
Open Outfile$ For Output As #2
On Error GoTo 0
'get the name/value pairs and place in variables for
'use in the query
Do While Not EOF(1)
Line Input #1, textline
GetNameValuePairs textline, srchDate, srchFrom, srchSubject, srchMessage
GetServerName textline, envServerName
Loop
The work of reading the Infile takes place in the Do/While loop. As the file is read in the loop, the name/value pairs and server name are looked for and saved in the appropriate variables.
The following code demonstrates the technique for obtaining the value of a given named variable, in this case the name of the HTTP server (that is, IP address).
Public Sub GetServerName(ByVal textline, ByRef envServerName)
strlen = Len(textline)
If InStr(textline, "SERVER_NAME") Then
CommaPos = InStr(textline, "=")
envServerName = Right$(textline, (strlen - CommaPos))
If Len(envServerName) = 0 Then envServerName = ""
End If
End Sub
Whatever is written to the Outfile will be sent back to the HTTP client. In the following example, a URL is written to the Outfile. When the server receives the contents of the Outfile by means of STDOUT (done by CGISPAWN), the server will redirect the client to the new URL.
Print #2, "Location: http://" + envServerName + "/" + indexfn + Chr(10)
Print #2, Chr(10)
In the following example, HTML text is written to the Outfile. When the server receives this data, the server will direct the data to the client as HTML text.
Print #2, "Content-type: text/html" + Chr(10)
Print #2, Chr(10)
Print #2, "<H1>VB Spitback</H1><p>"
Do While Not EOF(1)
Line Input #1, textline
Print #2, "<li> <code>" + textline + "</code>"
Loop
Print #2, "</ul>"
Note in the preceding two examples that the first line contains a server directive (Location and Content-type). This MUST be the first line in the Outfile. In addition, the first line must be followed by two line feeds (Chr(10)). Although obvious in this code, it is easy to forget and will cause you much grief if you fail to clue in.
Your application should terminate without user interaction. Once the application has done what it must to process the data passed in by the Infile, the application should terminate and thereby give control back to CGISPAWN. In Visual Basic, this is easily done by simply calling End, as the following example illustrates. Note that prior to terminating, the application must close all files that it has opened. You may recall that the files associated with #1 and #2 in the preceding examples were the Infile and the Outfile.
Close #2
Close #1
'must exit at this point
End
Graham, Ian S. HTML Sourcebook. New York, NY: Wiley, 1995.