The Microsoft Internet Information Server (IIS) is Microsoft's entry into the Web server arena. Being a Microsoft product, it is closely knit with the Windows NT operating system as well as Microsoft's BackOffice family of products.
The IIS supports CGI scripting applications but does not directly support the Win/CGI interface. This means that input and output take place using the standard input and standard output for an NT console application. This is something I've never been able to accomplish using Visual Basic (not that I ever had the desire to give it a serious effort).
Like most Microsoft products, the IIS is supported by an API. This API is known as the Internet Server API, or ISAPI. The IIS provides the capability of running DLLs as server extensions. The advantages of using DLLs versus standard executables are discussed in Chapter 10, "Using OLEISAPI with the Microsoft Internet Information Server." However, they can be summed up by stating that using a DLL instead of an EXE greatly improves the server's and the extension's performance. Microsoft's position is that your Win/CGI applications should be converted to ISAPI applications when used on the IIS. I second this recommendation.
Obviously, the previous two paragraphs create a quandary for the Webmaster who has a lot of Win/CGI applications written in VB but wants to switch Web servers to use the IIS. That's where Jim Schmidt, a support engineer at Microsoft Developer Support, came to the rescue. He authored the IS2WCGI.DLL sample application contained in Microsoft's ActiveX SDK (formerly known as the Internet SDK).
Please don't throw tomatoes my way, but you're about to encounter some C++ code. You probably realized that when I started talking about DLLs, but I like to warn the unsuspecting anyway.
Just when I had about given up hope of being able to use VB to code applications for the IIS, I stumbled upon a reference to the IS2WCGI sample server extension. Everywhere else in the IIS documentation, I saw those three dreaded letters: DLL, also spelled C++.
The IS2WCGI sample allows you to use your existing Win/CGI applications on the Internet Information Server. It is an ISAPI-compliant DLL that takes the data received from the IIS, puts it into a Win/CGI-compliant format, and launches the Win/CGI executable. IS2WCGI then sends all the Win/CGI application's output data back to the IIS to return it to the client.
The IS2WCGI application was developed to allow existing applications to be used on the IIS. However, keep in mind that the reason the IIS does not directly support the Win/CGI is that the specification is very inefficient. The ISAPI provides a very efficient means of launching the server extension and providing it with the necessary data from the client. Therefore, the IS2WCGI method should only be used until your applications can be ported to ISAPI or converted to an OLE server to use the OLEISAPI extension discussed in Chapter 10.
The IS2WCGI sample also includes a sample Web page (see Figure C.1) and a C++ application that returns all the Win/CGI data to the client, similar to the application developed in Chapter 7, "Creating CGI Applications in Visual Basic."
Figure C.1. The Web page included with the IS2WCGI sample.
The remainder of this appendix consists of an elementary discussion of the code (being C++-impaired, that's all I'm capable of anyway!) and some instructions on installing and referencing the IS2WCGI DLL on your Internet Information Server.
The code for the IS2WCGI sample, even though it's written in C++, is pretty easy to follow. As I said earlier, I won't rehash the entire application here. Instead, I'll touch some of the highlights and areas you may wish to modify for your applications.
The ISAPI specification requires that extension DLLs provide and export two functions: GetExtensionVersion() and HttpExtensionProc(). These functions must be present in every ISAPI-compliant DLL you code.
When the server loads the DLL, it calls the DLL's GetExtensionVersion() to retrieve the version of the specification on which the extension is based and a description for administrators. Every time a client requests the URL specifying the extension DLL, the HttpExtensionProc() function is called. This is where the bulk of the work in an ISAPI extension is performed. It's identical to the Sub Main procedure of the Win/CGI applications developed in this book.
As explained in Chapter 10, the server communicates with the DLL using a structure of the type EXTENSION_CONTROL_BLOCK. This structure contains information about the current request message and about the server itself. A pointer to an EXTENSION_CONTROL_BLOCK structure is provided as the parameter to the HttpExtensionProc() function.
The IS2WCGI sample performs all of its work within HttpExtensionProc(). The DLL creates a temporary file to store the CGI data, fills the profile's key names, retrieves the encoded form data, and writes the data to either the profile file or to an external file (depending on the format of the data). The DLL then creates a child process used to execute the actual CGI application and waits for the process to finish execution. After the CGI application ends, the DLL writes the data back to the Web client and cleans up after itself.
While the IS2WCGI DLL functions just fine as provided with the ActiveX SDK, you may wish to make a few changes to the C++ code. To recompile the code, however, requires Visual C++. The Microsoft Developer Studio included with VC++ 4 makes working with C++ code almost bearable for the VB programmer. It's easy to move around in your C++ projects by using the Studio's class view, and the online books are very helpful. So, if you're going to commit to the Internet Information Server platform and want to write some extensions for your Web server, invest in a copy of VC++ 4.
The first thing I noticed when using the IS2WCGI extension was that the User Agent request message field was missing from the [CGI] section of the profile file. The first section to follow discusses adding this to the [CGI] section of the Win/CGI profile file. The sections that follow discuss increasing the timeout period that IS2WCGI waits for your CGI application to complete and how to change IS2WCGI to use the Win/CGI Debug mode.
IS2WCGI has a subroutine named FillCGI which creates the [CGI] section of the profile file. However, the FillCGI procedure is missing the [CGI] section's User Agent field. This field is returned, instead, in the [Extra Headers] section with the key name "USER_AGENT". If your applications expect to find the User Agent field in the [CGI] section, it's a simple matter to add it there.
To add this field to the [CGI] section, locate the FillCGI subroutine in the IS2WCGI.CPP source code. Add the following line at the bottom of the routine:
GetVarAndWriteKey (pParam, szSection, TEXT("HTTP_USER_AGENT"), [ic:ccc] TEXT("User Agent"));
Fortunately, the code has been documented well enough to make such modifications easy to identify and easy to code.
Another modification you might want to make is to change the value of the WAIT_EXT_TIMEOUT constant. This constant defines how long the DLL will wait for the CGI application to complete before flagging a timeout error. If you know your application can take longer than the defined value (120 seconds), locate the line
#define WAIT_EXT_TIMEOUT 120000 // 120 secs
within IS2WCGI.CPP. Adjust the value defined to a reasonable value for your application. However, don't set the constant too high or you'll risk leaving the user in limbo for extended periods of time should your CGI application actually not exit properly.
The IS2WCGI DLL, by default, deletes all of the CGI files it creates and also the output file your CGI application creates. If you want to run your application in Debug Mode and leave these files intact, you need to remove the DeleteFile calls that appear in HTTPExtenionProc().
To do so, open the IS2WCGI.CPP source code file. Locate HTTPExtenionProc() and scroll to near the end of the routine. Find the following comment
// // Clean Up // // Delete the temp files made in form decoding
and comment out all of the code between the last comment and the comment that reads
// Clean up key list resources & delete content file
Leave all the code that appears after this comment intact. Comment out the following line:
DeleteFile (param.lpszContentFile);
Be very careful not to comment out the line immediately following this one, which begins with HeapFree.
Also comment out the two lines:
DeleteFile (param.szProfielName); DeleteFile (param.szOutputFileName);
Finally, move to the beginning of the source file. In this line,
static TCHAR gszDebugMode[] = TEXT("No");
replace the "No" with "Yes".
After you rebuild the DLL, it will leave all of the temporary files intact. Note that this should be a temporary measure on your part. Once you have the CGI application working properly, change the code back to its original state. If you want to get really fancy, you can place an entry in the system registry to control whether or not the DLL should be run in Debug mode. My C++ skills are not up to that level, so I chose not to pursue this route.
A compile-time flag in IS2WCGI.CPP determines whether or not the DLL recognizes a direct return CGI application. A CGI application that is using direct return is responsible for creating the HTTP response message headers. A typical CGI application would allow the Web server to create the HTTP response message headers. In this case, the CGI application is responsible only for the contents of the response (typically the HTML text).
There are cases, however, when you want to control the response message headers. These include returning error conditions to the client and redirecting the client to another resource.
For example, I have a Web page that contains a link to a demo copy of an application. Instead of providing the ZIP filename in the link, I provide the name of a CGI application. When the application executes, it places the query string from the link as well as other CGI fields into a database and redirects the user's Web browser to the ZIP file. The code to perform the redirection (using variables and routines from CGI32.BAS) is
OutputString "HTTP/1.0 301 REDIRECT" OutputString "Location: /newpage.htm" OutputString ""
To change the IS2WCGI extension to allow direct returns from your applications, remove the comment from the line that reads
//#define CONFORMANT_HEADER
which can be found near the top of the IS2WCGI.CPP source file. Rebuild the DLL and rename it to something that you'll recognize as supporting the direct return. If you attempt to use this version of the DLL with a CGI application that does not return all of the HTTP response headers, your application will not return a valid HTTP message to the client.
After you have compiled IS2WCGI.DLL, you are ready to install it along side your Win/CGI application. The IS2WCGI.DLL file must be renamed to match the name of your executable because it uses its filename when it executes the CreateProcess() function to launch the Win/CGI application. So, if your executable is named entry.exe, you can make a copy of IS2WCGI.DLL, name it entry.dll, and place it in the same directory as entry.exe. This directory needs to be a directory that allows execution but does not allow reads. Figure C.2 shows the setup screen for such a directory in the IIS Administration Tool.
Figure C.2. The /cgi-win directory setup for the IIS.
The second step to using the IS2WCGI.DLL on your Web site is to properly refer to it in your HTML documents. Instead of your documents referencing the executable file, as is the case for other Web servers running Win/CGI applications, if you're using IIS, you should reference the DLL file. For example,
<form method="POST" action="/cgi-win/entry.dll">
will POST the form's data to the resource located at /cgi-win/entry.dll. This is the copy of the IS2WCGI.DLL server extension you just created. When the Submit button is clicked, the IIS loads the DLL which creates the Win/CGI temporary files and launches entry.exe, the original Win/CGI application.
A hyperlink that performs a GET request would look similar:
<A HREF="/cgi-win/entry.dll?QueryString">Query</A>
This link, when clicked, will follow the same chain of events as the form Submit just discussed.