Writing CGI Applications in Visual Basic

ID: Q239588


The information in this article applies to:
  • Microsoft Internet Information Server versions 3.0, 4.0
  • Microsoft Internet Information Services version 5.0


SUMMARY

A Common Gateway Interface (CGI) application can be written in any programming language that can access both the environment variables and STDIN or STDOUT. Because of the powerful text handling capability of the Microsoft Visual Basic (VB) programming language, many Web developers want to write CGI programs in VB. This article illustrates the techniques for writing CGI applications in VB, and provides a simple VB CGI sample.


MORE INFORMATION

Note: Microsoft provides programming examples for illustration only, without warranty either expressed or implied, including, but not limited to, the implied warranties of merchantability and/or fitness for a particular purpose. This article assumes that you are familiar with the programming language being demonstrated and the tools used to create and debug procedures. Microsoft support professionals can help explain the functionality of a particular procedure, but they will not modify these examples to provide added functionality or construct procedures to meet your specific needs. If you have limited programming experience, you may want to contact a Microsoft Certified Solution Provider or the Microsoft fee-based consulting line at (800) 936-5200. For more information about Microsoft Certified Solution Providers, please see the following page on the World Wide Web:

http://www.microsoft.com/mcsp/
For more information about the support options available from Microsoft, please see the following page on the World Wide Web:

http://www.microsoft.com/support/supportnet/overview/overview.asp

Retrieving Environment Variables

To retrieve an environment variable, use the Environ$ function in VB as follows:
VALUE = Environ$(NAME)
 
Where NAME is the environment variable that you want to retrieve. Its value is returned in VALUE.

Reading from STDIN and Writing to STDOUT

Use the Win32 APIs ReadFile to read from STDIN and WriteFile to write to STDOUT. These functions require you to provide a handle to STDIN or STDOUT. You can use the GetStdHandle function to get the handles to STDIN or STDOUT. In this article, aliasing is used on the GetStdHandle APIs to simplify the function calls. The declarations for these functions are as follows:

Public Declare Function stdin Lib "kernel32" Alias "GetStdHandle" _
(Optional ByVal Handletype As Long = STD_INPUT_HANDLE) As Long

Public Declare Function stdout Lib "kernel32" Alias "GetStdHandle" _
(Optional ByVal Handletype As Long = STD_OUTPUT_HANDLE) As Long

Public Declare Function ReadFile Lib "kernel32" _
(ByVal hFile As Long, ByVal lpBuffer As Any, ByVal nNumberOfBytesToRead As Long, _
lpNumberOfBytesRead As Long, Optional ByVal lpOverlapped As Long = 0&) As Long

Public Declare Function WriteFile Lib "kernel32" _
(ByVal hFile As Long, ByVal lpBuffer As Any, ByVal nNumberOfBytesToWrite As Long, _
lpNumberOfBytesWritten As Long, Optional ByVal lpOverlapped As Long = 0&) As Long 
The constants passed to GetStdHandle are defined as:

Public Const STD_INPUT_HANDLE = -10&
Public Const STD_OUTPUT_HANDLE = -11& 
Please refer to the MSDN documentation for the definition of each parameter in those functions. In the following sample, a full list of CGI environment variables is made constant. This lists all the constants together on a Ctrl-J. It also eliminates programming errors by providing compiler and IntelliSense validation, yet does not preclude you from entering your own strings.

Sample Code

For simplicity, error trapping is omitted in the following sample (HELLO.BAS):

Option Explicit

Public Const STD_INPUT_HANDLE = -10&
Public Const STD_OUTPUT_HANDLE = -11&

Public Const CGI_AUTH_TYPE         As String = "AUTH_TYPE"
Public Const CGI_CONTENT_LENGTH    As String = "CONTENT_LENGTH"
Public Const CGI_CONTENT_TYPE      As String = "CONTENT_TYPE"
Public Const CGI_GATEWAY_INTERFACE As String = "GATEWAY_INTERFACE"
Public Const CGI_HTTP_ACCEPT       As String = "HTTP_ACCEPT"
Public Const CGI_HTTP_REFERER      As String = "HTTP_REFERER"
Public Const CGI_HTTP_USER_AGENT   As String = "HTTP_USER_AGENT"
Public Const CGI_PATH_INFO         As String = "PATH_INFO"
Public Const CGI_PATH_TRANSLATED   As String = "PATH_TRANSLATED"
Public Const CGI_QUERY_STRING      As String = "QUERY_STRING"
Public Const CGI_REMOTE_ADDR       As String = "REMOTE_ADDR"
Public Const CGI_REMOTE_HOST       As String = "REMOTE_HOST"
Public Const CGI_REMOTE_USER       As String = "REMOTE_USER"
Public Const CGI_REQUEST_METHOD    As String = "REQUEST_METHOD"
Public Const CGI_SCRIPT_NAME       As String = "SCRIPT_NAME"
Public Const CGI_SERVER_NAME       As String = "SERVER_NAME"
Public Const CGI_SERVER_PORT       As String = "SERVER_PORT"
Public Const CGI_SERVER_PROTOCOL   As String = "SERVER_PROTOCOL"
Public Const CGI_SERVER_SOFTWARE   As String = "SERVER_SOFTWARE"

Public Declare Function Sleep Lib "kernel32" _
(ByVal dwMilliseconds As Long) As Long

Public Declare Function stdin Lib "kernel32" Alias "GetStdHandle" _
(Optional ByVal Handletype As Long = STD_INPUT_HANDLE) As Long

Public Declare Function stdout Lib "kernel32" Alias "GetStdHandle" _
(Optional ByVal Handletype As Long = STD_OUTPUT_HANDLE) As Long

Public Declare Function ReadFile Lib "kernel32" _
(ByVal hFile As Long, ByVal lpBuffer As Any, ByVal nNumberOfBytesToRead As Long, _
lpNumberOfBytesRead As Long, Optional ByVal lpOverlapped As Long = 0&) As Long

Public Declare Function WriteFile Lib "kernel32" _
(ByVal hFile As Long, ByVal lpBuffer As Any, ByVal nNumberOfBytesToWrite As Long, _
lpNumberOfBytesWritten As Long, Optional ByVal lpOverlapped As Long = 0&) As Long

Sub Main()

    Dim sReadBuffer As String
    Dim sWriteBuffer As String
    Dim lBytesRead As Long
    Dim lBytesWritten As Long
    Dim hStdIn As Long
    Dim hStdOut As Long
    Dim iPos As Integer
    
    ' sleep for one minute so the debugger can attach and set a break
    ' point on line below
    ' Sleep 60000
    
    sReadBuffer = String$(CLng(Environ$(CGI_CONTENT_LENGTH)), 0)
    
    ' Get STDIN handle
    hStdIn = stdin()
    ' Read client's input
    ReadFile hStdIn, sReadBuffer, Len(sReadBuffer), lBytesRead

    ' Find '=' in the name/value pair and parse the buffer
    iPos = InStr(sReadBuffer, "=")
    sReadBuffer = Mid$(sReadBuffer, iPos + 1)
    
    ' Construct and send response to the client
    sWriteBuffer = "HTTP/1.0 200 OK" & vbCrLf & "Content-Type: text/html" & _
                    vbCrLf & vbCrLf & "Hello "
    hStdOut = stdout()
    WriteFile hStdOut, sWriteBuffer, Len(sWriteBuffer) + 1, lBytesWritten
    WriteFile hStdOut, sReadBuffer, Len(sReadBuffer), lBytesWritten

End Sub 

HTML form to test the CGI (FOO.HTM)

<HTML>
<HEAD>
<TITLE>Testing VB CGI</TITLE>
</HEAD>
<BODY>
<FORM action="/cgi-bin/hello.exe" method="POST">
<INPUT TYPE="TEXT" NAME="Name"> Name<BR>
<INPUT TYPE="SUBMIT">
</FORM>
</BODY>
</HTML> 

Steps to build the simple CGI Hello.exe:

  1. Create a New Project as Stand exe.


  2. Remove Form from the Project.


  3. Add a Module to the Project and name it HELLO.


  4. Set Sub Main as the Startup Object (under Project Properties menu item).


  5. Copy the above VB code and paste it to the Module.


  6. Make Hello.exe.


Notes:
  • The code above demonstrates how to handle HTTP POST request. To handle GET request, the CGI application needs to retrieve the QUERY_STRING environment variable. The QUERY_STRING variable contains name/value pairs separated by the & in the format "Name=Joe&Color=Red." Note that URL encoding is used, all spaces are converted to +, and all special characters such as ! are converted to their HEX ASCII values. In other words, "Hello, World!" string is represented as "Hello,+World%21." Visual Basic CGI applications have to implement all the parsing code.


  • Because the CGI application is started by the service, it may not be able to access network shares.


  • Be aware that CGI runs as a service, which communicates with the server. Therefore, visual interface forms, controls, and message boxes are completely meaningless. As a matter of fact, a message box will cause a CGI application to stop responding.


  • Error handling should be conducted throughout a CGI code in Visual Basic, so that the default error message box is not be displayed. You can either log error messages on the server or write them to the user's browser.


  • Visual C debuggers can debug applications written in Visual Basic. Therefore, you can use the CGI debugging techniques referenced below. To debug a VB application with VC, choose Compile to Native Code, and then select Create Symbolic Debug Info and No Optimization. When it is completed and the .exe is generated, Visual C can attach to the running CGI application written in Visual Basic.


  • To test a CGI application, copy it to the IIS virtual directory with Execute permissions.


  • Be aware that runtime errors or dialog boxes in the Visual Basic code may cause the CGI to stop responding. If the CGI stops responding, it can be run in the Visual Studio debugger. For additional information, click the article number below to view the article in the Microsoft Knowledge Base:
    Q238788 How to Debug CGI Applications Running Under IIS



REFERENCES

RFC 2068, Hypertext Transfer Protocol
RFC 1738, Uniform Resource Locators (URL).
The Common Gateway Interface (http://hoohoo.ncsa.uiuc.edu/cgi)

Q238788 How to Debug CGI Applications Running Under IIS
Q207671 Accessing Network Files from IIS Applications
Q239535 Debug CGI Using VC Just-in-time (JIT) Debugger

Additional query words: iis

Keywords : kbGrpInetServer
Version : winnt:3.0,4.0
Platform : winnt
Issue type : kbinfo


Last Reviewed: February 2, 2000
© 2000 Microsoft Corporation. All rights reserved. Terms of Use.