在 Visual Basic 中编写 CGI 应用程序

本文介绍如何编写 CGI 应用程序。

原始产品版本: Visual Basic
原始 KB 数: 239588

概要

通用网关接口(CGI)应用程序可以采用任何编程语言编写,这些编程语言可以访问环境变量和 STDIN 或 STDOUT。 由于 Visual Basic 编程语言具有强大的文本处理功能,许多 Web 开发人员想要在 Visual Basic 中编写 CGI 程序。 本文演示了在 Visual Basic 中编写 CGI 应用程序的技术,并提供简单的 Visual Basic CGI 示例。

详细信息

注释

Microsoft仅提供用于说明的编程示例,而不提供明示或暗示担保。 包括但不限于适销性或为特定目的的适合性的隐含担保。 本文假定你熟悉正在演示的编程语言以及用于创建和调试过程的工具。 Microsoft支持工程师可以帮助解释特定过程的功能,但它们不会修改这些示例,以提供附加的功能或构造过程以满足你的特定要求。

检索环境变量

若要检索环境变量,请使用 Environ$ Visual Basic 中的函数,如下所示:

VALUE = Environ$(NAME)

注释

NAME 是要检索的环境变量。 其值在VALUE中返回。

从标准输入 (STDIN) 读取数据并写入到标准输出 (STDOUT)

使用 Win32 ReadFile 函数从 STDIN 读取,并使用 WriteFile 函数写入 STDOUT。 这些函数需要你提供 STDINSTDOUT 的句柄。 可以使用GetStdHandle函数获取STDINSTDOUT的句柄。 在本文中,对函数使用 GetStdHandle 别名来简化函数调用。 这些函数的声明如下所示:

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

传递给函数的 GetStdHandle 常量定义为:

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

有关这些函数中每个参数的定义,请参阅 MSDN 文档。 在以下示例中,CGI 环境变量的完整列表是常量。 按下 Ctrl-J 键将列出所有常量。 它还通过提供编译器和 IntelliSense 验证来消除编程错误,但不会阻止你输入自己的字符串。

代码示例

为了简单起见,以下示例(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

用于测试 CGI 的 HTML 表单(Test.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>

生成 CGI Hello.exe 文件的步骤:

  1. 将新项目创建为标准 .exe 项目。

  2. 从项目中删除窗体。

  3. 将模块添加到项目并将其命名为 HELLO

  4. Sub Main 设置为启动对象(在项目属性中)。

  5. 复制上述 Visual Basic 代码并将其粘贴到模块。

  6. 生成 Hello.exe

    注释

    • 示例代码演示如何处理 HTTP POST 请求。 若要处理 GET 请求,CGI 应用程序需要检索 QUERY_STRING 环境变量。 变量 包含以 & 分隔的名称/值对,采用格式 。 使用 URL 编码,所有空格都转换为 + ,以及所有特殊字符,如 ! 转换为其十六进制 ASCII 值。 换句话说,“Hello, World!” 字符串表示为“Hello,+World%21”。Visual Basic CGI 应用程序必须实现所有分析代码。

    • 由于 CGI 应用程序由服务启动,因此可能无法访问网络共享。

    • 请注意,CGI 作为服务运行,该服务与服务器通信。 因此,可视界面窗体、控件和消息框是完全毫无意义的。 事实上,消息框将导致 CGI 应用程序停止响应。

    • 应在 Visual Basic 中在整个 CGI 代码中执行错误处理,以便不显示默认错误消息框。 可以在服务器上记录错误消息,也可以将其写入用户的浏览器。

    • Visual C 调试器可以调试以 Visual Basic 编写的应用程序。 因此,可以使用下面引用的 CGI 调试技术。 若要使用 Visual C 调试 Visual Basic 应用程序,请选择“编译为本机代码”,然后选择“创建符号调试信息”和“无优化”。 完成应用程序的构建并生成 .exe 文件后,Visual C 可以附加到以 Visual Basic 编写的正在运行的 CGI 应用程序。

    • 若要测试 CGI 应用程序,请使用执行权限将其复制到 IIS 虚拟目录。

    • 请注意,Visual Basic 代码中的运行时错误或对话框可能会导致 CGI 应用程序停止响应。 如果 CGI 应用程序停止响应,则可以在 Visual Studio 调试器中运行它。

References