Chapter 6

Understanding ISAPI Extensions


The Internet server application programming interface (ISAPI) can be used to create extensions, often called server applications, to Microsoft's Internet Information Server (IIS) or any hypertext transport protocol (HTTP) server compatible with the ISAPI interface.

ISAPI extensions give Web developers an alternative to the common gateway interface (CGI) for creating interactive Web sites.

Unlike CGI applications, which are stand-alone executables, ISAPI extensions are used as Win32 DLLs that are dynamically loaded by IIS on request. Using ISAPI extensions offers increased performance and resource use compared to CGI applications.

Developing a CGI application is straightforward because CGI applications are independent of the server using the application. CGI applications process client requests by retrieving the input arguments from the application's standard input (stdin) when it is started.

All output from a CGI application is returned through the application's standard output (stdout). CGI developers can develop and debug their applications without regard to the interaction of other processes such as an HTTP server.

Since ISAPI extensions are used as DLLs, the extensions are dependent on the interaction with the applications using them. To get the maximum benefits from ISAPI extensions, the developer should have a through understanding of how extensions work and how extensions are used by HTTP servers.

In this chapter you learn the following:

How ISAPI Extensions Work

ISAPI extensions are a new approach for customizing the interactive capabilities of your Web site. An ISAPI extension is a specification which improves on the CGI that Web servers have used for years. ISAPI extensions also provide an entirely new capability for Web servers.

What does an ISAPI extension do? An ISAPI extension (and a CGI application) is a standardized way to pass data that users enter in their Web browsers to back-end programs that you provide on your Web server. This is typically done through Web fill-in forms but can be used for other communication mechanisms, including HTTP GETs.

A common example that illustrates this concept is when you do a search for information on the Internet. The information you are searching for is collected and passed into a back-end program, which then returns the results. The back-end program is typically a CGI script or an ISAPI extension.

Figure 6.1 shows a search form from the AltaVista search engine.

Fig. 6.1

Fill-in search form used by AltaVista for surfing the Net.

An ISAPI extension is a more than capable replacement for CGI applications. ISAPI extensions can provide all of the capabilities of a CGI application, plus the additional customization hooks that CGI applications cannot provide.

Before looking inside ISAPI extensions, we look briefly at the flow of a CGI application to provide a meaningful comparison.

Examining the Flow of a CGI Application

CGI is an interface for running external programs (called gateways) on a Web server. Only HTTP Web servers are compatible.

The external programs are called gateways because they allow browser applications access to information that is not readable by the client. In other words, the gateways do the following:

Figure 6.2 illustrates this concept.

Fig. 6.2

A CGI application acts as an information translator for the client browser and Web server.

A Web server responds to a CGI execution request from a client browser by creating a new process and then passing the data received from the browser through environment variables and stdin. Results gathered by the CGI application are expected on the stdout of the newly created process. The WEB server creates as many processes as the number of requests received.

To recap the flow of a CGI application, the four steps are as follows:

[Note]


For more information on the CGI specification, see http://hoohoo.ncsa.uiuc.edu/cgi/.


Drawbacks to the CGI Architecture

A glaring shortcoming of the CGI architecture is that a separate process must be invoked whenever a request is received for the gateway. Starting separate processes can be time-consuming and need large amounts of memory.


[Note]


If your Web site gets many requests for the gateway, starting an instance of the CGI application for each request can quickly consume memory. This could lead to heavy disk I/O if there is not enough memory for the number of processes the server creates.


Although the CGI interface is workable, you can imagine the bottlenecks for heavily traveled Web servers. PC Week recently documented that the Microsoft Web site was processing over 40 million requests daily!

ISAPI extensions were introduced to provide CGI function while reducing the resources for filling browser requests.

An important difference between ISAPI extensions and CGI applications is that an ISAPI extension is not a separate executable. An ISAPI extension is a DLL that is loaded into memory by the server and accessed through a defined set of entry points.

Dynamic linking provides a way for a process, in this case IIS, to call a function that is not part of its executable code. The code in a DLL is compiled and linked separately from the executable that uses its code. When a DLL is called, the code in the library is shared among all processes that are using the DLL.

An example of a DLL is the Win32 API, which is used as a number of DLLs. All 32-bit applications running under Windows NT or Windows 95 use one set of Win32 API code.


[Caution]


Since multiple applications can simultaneously use the code in a DLL, you must make sure that the DLL is thread-safe. Thread-safe means that any data or resources accessed from within the DLL must be protected from simultaneous calls from another thread or even another executable.


Chapter 19 "Making Your Extensions Thread-Safe," covers techniques for building thread-safe DLLs.


The two methods for calling a function in a DLL are as follows:

[Note]


Runtime linking is possible because ISAPI defines known entry points that must be present in every ISAPI extension. The presence of these defined entry points allows the server to load and execute the code in any extension. ISAPI extensions would not be possible if every extension DLL consisted of different user-defined entry points.


Works Like a CGI Executable

The following sequence explains how an ISAPI extension DLL interacts with an HTTP server. As with a CGI application, there are four steps for a browser to use an ISAPI extension. These steps are as follows:

Gets Program Control

When an extension is called by the server, program control for the calling thread is transferred to the extension. Even though the extension is dynamically loaded into the server's memory, the HTTP requests are processed as though the extension is a part of the server.

Since a server can process several client requests simultaneously, be sure that internal resources used by the extension are protected from access by multiple threads of execution.

Once control is transferred to the extension, control does not return to the server until a response is prepared and returned by the extension to the server.

ISAPI extensions are used as DLLs. Control is transferred from the server to the extension-that is, the extensions execute in the same process as the server. For this reason, it is important to thoroughly test and debug extensions before using them at a Web site.

An access violation by the extension can cause the server to crash, keeping the WEB site from further use until the server is restarted.

ISAPI extensions can also corrupt the server's memory space or cause memory leaks if the extensions fail to clean up properly after themselves.


[Note]


Many server developers protect against access violations inside ISAPI extensions by wrapping the extension entry points within try/except blocks. This way, access violations or other exceptions in the extension cannot cripple the server. The extension, however, must still be thoroughly tested and debugged.


Examining the Environment

Once the ISAPI extension is loaded into memory, the extension examines its environment. Whereas a CGI application retrieves information about the environment from environment variables and stdin, an extension retrieves it's information from a data structure called an extension control block (ECB). Figure 6.3 illustrates the interaction between a server and an extension.

Fig. 6.3

ECBs are used to transfer information between servers and ISAPI extensions.

The ECB holds information used by the extension, including the client query string, path information, method name, and translated path.

ECBs, and the interaction between an server and extension, are explained in further detail in later in this chapter under, "Passes Control to the Extension."

Returning Custom-Generated Content

After the client request is processed by the extension, the results are sent back to the client. In a CGI application, the results are written to stdout. In an ISAPI extension, the results can be written back to a client in two ways.

The first is like writing to stdout. This is done by calling WriteClient, a callback function to the server in which the server passes a preallocated buffer. The extension uses the buffer to return the response.

If a completion status needs to be returned with the response, the client can call the ServerSupportFunction.

An explanation of how results are returned from an extension can be found later in this chapter under "Passes Control to the Extension."

Loading on First Request

Since an ISAPI extension is used as a DLL, the extension is dynamically loaded into the server's process space when the first request for the extension is received.

As additional requests for the extension are received, the overhead for calling the extension is minimal since the extension is already loaded into memory. This is in direct contrast to CGI applications, which start another instance of the application for each client request.

The ISAPI architecture allows for multiple extensions to co-exist in the server. Typically, extensions are kept in memory until the server shuts down.


[Note]


Since the server knows what extensions are loaded into memory, the server can unload extensions that have not been used for a specified amount of time.


Naming Explicitly

When an extension is to be used by a client, the client must explicitly call for the extension. A sample HTTP request would look like this:

http://www.test.com/scripts/foo.dll?Parm1+Parm2

In this sample, the client is requesting the extension entitled foo.dll, with the parameters PARM1 and PARM2 to be passed to the extension.

When the HTTP request is made by the client, the server recognizes that foo.dll is an application extension and loads the extension into memory (if the extension is not already loaded).

Once the extension is loaded, the input parameters are passed into the extension for processing. The output response is then returned to the client.


[Note]


Beginning with IIS 2.0, ISAPI extensions are recognized with the extension .ISA instead of .DLL. The extension of .DLL is still recognized by both versions of IIS.


Flow of an ISAPI Extension

To understand how to use and develop ISAPI extensions, you need to know how an ISAPI extension works and is used by a server. Specifically, you need to understand the flow of information, starting with the client request and ending with the page returned to the client.

If you compare the flow of an ISAPI extension with the flow of a CGI application, you see that they are similar in nature. However, each achieves its results differently.


[Crossreference]


See "Examining the Flow of a CGI Application," in Chapter 7.


The Browser Requests ISAPI Processing

The use of an ISAPI extension begins with a client request to use the extension. The client issues an HTTP request such as

http://www.xyz.com/scripts/test.isa?PARM1+PARM2

The server recognizes the .ISA extension as an ISAPI extension and starts the sequence of events for using extensions. Notice that as in CGI applications, input parameters can be passed into an extension for processing.

Loading the ISAPI Extension

ISAPI extensions are linked dynamically at runtime by the server through the Win32 API LoadLibrary. Every ISAPI extension must have two defined entry points: GetExtensionVersion and HttpExtensionProc. The function pointers for the entry points are retrieved by the server through the GetProcAddress API.

Unlike CGI applications, ISAPI extensions are loaded in the same address space as the server. This means all the resources that are made available by the server process are also available to the ISAPI extensions. Minimal overhead is needed to execute the extensions because there is no additional overhead for each request.

Loading an ISAPI extension is typically faster than starting a separate executable. Context and task switching are reduced to a minimum since the extension is loaded in the memory space of the server. Also, the same extension can serve multiple requests, making ISAPI extensions a more scaleable solution for active Web sites.

Since a server knows the ISAPI extension DLLs that are already in memory, the server can unload the ISAPI extension DLLs that have not been accessed for a specified amount of time. This is a configurable parameter for the extension.

A server can even speed up the first request to the ISAPI extension by preloading an ISAPI extension DLL. Also, unloading ISAPI extension DLLs that have not been used for some time frees system resources.

Passing Control to the Extension

When the server loads the extension, it calls the entry point GetExtensionVersion to retrieve the version number of the specification on which the extension is based. GetExtensionVersion also provides a short text description of the extension, which is useful for server administrators.

The prototype for GetExtensionVersion is shown below.

BOOL WINAPI GetExtensionVersion( HSE_VERSION_INFO *pVer )

typedef struct _HSE_VERSION_INFO {

DWORD dwExtensionVersion;

CHAR lpszExtensionDesc[HSE_MAX_EXT_DLL_NAME_LEN];

} HSE_VERSION_INFO, *LPHSE_VERSION_INFO;

#define HSE_VERSION_MAJOR 2 // major version of this spec

#define HSE_VERSION_MINOR 0 // minor version of this spec

#define HSE_MAX_EXT_DLL_NAME_LEN 256

GetExtensionVersion takes one input parameter, a pointer to an HSE_VERSION_INFO structure. This structure has two member variables: dwExtensionVersion, which holds the version of ISAPI being used, and lpszExtensionDesc, which holds a description of the extension's function. The recommended use of this function is shown in Listing 6.1.

Listing 6.1 Typical Use of GetExtensionVersion

BOOL WINAPI GetExtensionVersion( HSE_VERSION_INFO *pVer )

{

pVer->dwExtensionVersion = MAKELONG( HSE_VERSION_MINOR,

HSE_VERSION_MAJOR );

lstrcpyn( pVer->lpszExtensionDesc,

"This is a sample Web Server Application",

HSE_MAX_EXT_DLL_NAME_LEN );

return TRUE;

}

IIS Returns a Page to the Browser

Interaction between a server and an ISAPI extension DLL is through an ECB, as discussed earlier in this chapter under "Examines the Environment."

A client uses an ISAPI extension just as its CGI counterpart does, except that instead of referencing

http://www.xyz.com/scripts/foo.exe?Param1+Param2

in the CGI instance, it uses the form

http://www.xyz.com/scripts/foo.isa?Param1+Param2

This means that in addition to identifying the files with the extensions .EXE and .BAT as CGI executable files, the server also identifies a file with an .ISA extension as a script to execute.

All client requests are serviced through the entry point, HttpExtensionProc. Information needed by the extension is passed to the extension by an ECB.

A pointer to an ECB is passed as an input parameter to the HttpExtensionProc function. The extension gets commonly needed information such as the query string, path information, method name, and the translated path.

DWORD HttpExtensionProc( LPEXTENSION_CONTROL_BLOCK *lpEcb );

The ECB is represented through a data structure called the EXTENSION_CONTROL_BLOCK shown below.

typedef struct _EXTENSION_CONTROL_BLOCK {

DWORD cbSize; // Size of this struct.

DWORD dwVersion; // Version info of this spec

HCONN ConnID; // Context number not to be

modified!

DWORD dwHttpStatusCode; // HTTP Status code

CHAR lpszLogData[HSE_LOG_BUFFER_LEN];/

/ null terminated log info specific to this Extension DLL

LPSTR lpszMethod; // REQUEST_METHOD

LPSTR lpszQueryString; // QUERY_STRING

LPSTR lpszPathInfo; // PATH_INFO

LPSTR lpszPathTranslated; // PATH_TRANSLATED

DWORD cbTotalBytes; // Total bytes indicated from

client

DWORD cbAvailable; // Available number of bytes

LPBYTE lpbData; // Pointer to cbAvailable

bytes

LPSTR lpszContentType; // Content type of client data

BOOL (WINAPI * GetServerVariable) ( HCONN hConn,

LPSTR

lpszVariableName,

LPVOID lpvBuffer,

LPDWORD

lpdwSizeofBuffer );

BOOL (WINAPI * WriteClient) ( HCONN ConnID,

LPVOID Buffer,

LPDWORD lpdwBytes,

DWORD dwReserved );

BOOL (WINAPI * ReadClient) ( HCONN ConnID,

LPVOID lpvBuffer,

LPDWORD lpdwSize );

BOOL (WINAPI * ServerSupportFunction)( HCONN hConn,

DWORD dwHSERRequest,

LPVOID lpvBuffer,

LPDWORD lpdwSize,

LPDWORD

lpdwDataType );

}

The ECB has the following fields:

Field (parameter direction)

Description

cbSize (IN)

The size of this structure.

dwVersion (IN)

The ISAPI version that the server is using. The HIWORD has the major version number and the LOWORD has the minor version number.

connID (IN)

A unique connection number assigned by the server, which should not be changed.

dwHttpStatusCode (OUT)

The status of the current transaction when the request is completed.

lpszLogData (OUT)

A buffer of the HSE_LOG_BUFFER_LEN size. The buffer holds a null-terminated, log information string of the current transaction. This log information, which is specific to the ISAPI extension, will be entered in the server log.

Maintaining a single log file with both server and ISAPI extensions transactions is useful for administration.

lpszMethod (IN)

The method with which the request was made. This is equivalent to the CGI variable REQUEST_METHOD.

lpszQueryString (IN)

A null-terminated string holding the query information. This is equivalent to the CGI variable QUERY_STRING.

lpszPathInfo (IN)

A null-terminated string holding extra path information given by the client. This is equivalent to the CGI variable PATH_INFO.

lpszPathTranslated (IN)

A null-terminated string holding the translated path. This is equivalent to the CGI variable PATH_TRANSLATED.

cbTotalBytes (IN)

The total number of bytes to be received from the client. This is equivalent to the CGI variable CONTENT_LENGTH.

If this value is 0xffffffff, there are 4 gigabytes or more of available data. In this case, ReadClient should be called until no more data is returned.

cbAvailable (IN)

The available number of bytes (out of a total of cbTotalBytes) in the buffer pointed to by lpbData. If cbTotalBytes is the same as cbAvailable, the lpbData variable points to a buffer that holds all the data as sent by the client.

Otherwise, cbTotalBytes holds the total number of bytes of data received. The ISAPI extension uses the ReadClient callback function to read the rest of the data (beginning from an offset of cbAvailable).

lpbData (IN)

This points to a buffer of the cbAvailable size that holds the data sent by the client. The extension receives the first 48K of data.

lpszContentType (IN)

A null-terminated string holding the content type of the data sent by the client. This is equivalent to the CGI variable CONTENT_TYPE.

BOOL (WINAPI *GetServerVariable)

BOOL (WINAPI *WriteClient)

BOOL (WINAPI *ReadClient)

BOOL (WINAPI *ServerSupportFunction)

These are pointers to callback functions used by the server. GetServerVariable and ReadClient are functions used to retrieve information from the server. WriteClient and ServerSupportFunction are used to send a properly formatted response to the server.

The HttpExtensionProc function is analogous to main for a CGI application. In other words, HttpExtensionproc is the main processing body for an ISAPI extension. After the processing of the client request is finished, HttpExtensionProc can have the following return values.

Return Value

Description

HSE_STATUS_SUCCESS

The extension has finished processing. The server can disconnect and free allocated resources.

HSE_STATUS_SUCCESS_AND_KEEP_CONN

The extension has finished processing and the server should wait for the next HTTP request if the client uses persistent connections.

The extension should return this only if it was able to send the right content-length header to the client. The server does not have to keep the session open.

The extension should return this value only if it has sent a connection: a keep-alive header to the client.

HSE_STATUS_PENDING

The extension has queued the request for processing and will notify the server when it has finished.

HSE_STATUS_ERROR

The extension has found an error while processing the request. The server can disconnect and free allocated resources.

The GetServerVariable function copies information into a buffer supplied by the client. The information includes CGI variables relating to an HTTP connection or to the server itself.

The information requested is passed as a string through the lpzVariableNames parameter. Possible values include the following:

AUTH_TYPE-The type of authentication used. For example, if basic authentication is used, the string is "basic." For NT challenge-response, it is "NTLM."

Other authentication schemes have other strings. Since new authentication types can be added to the server, we can't list all the string possibilities. If the string is empty, no authentication is used.

CONTENT_LENGTH-The number of bytes that the script can expect to receive from the client.

CONTENT_TYPE-The content type of the information supplied in the body of a POST request.

PATH_INFO-Additional path information, as given by the client. This consists of the trailing part of the URL, after the script name but before the query string, if any.

PATH_TRANSLATED-The value of PATH_INFO, but with any virtual path name expanded into a directory specification.

QUERY_STRING-The information that follows the "?" in the URL that referenced this script.

REMOTE_ADDR-The IP address of the client or agent of the client-for example, the gateway that sent the request.

REMOTE_HOST-The host name of the client or agent of the client-for example, the gateway that sent the request.

REMOTE_USER-The user name supplied by the client and authenticated by the server. This comes back as an empty string when the user is anonymous (but authenticated).

UNMAPPED_REMOTE_USER-The user name before any ISAPI extensionPI filter mapped the user, making the request to an NT user account. (The NT user account appears as REMOTE_USER.)

REQUEST_METHOD-The HTTP request method.

SCRIPT_NAME-The name of the script program being executed.

SERVER_NAME-server's host name or IP address, as it should appear in self-referencing URLs.

SERVER_PORT-TCP/IP port on which the request was received.

SERVER_PORT_SECURE-A string of either 0 or 1. If the request is being handled on the secure port, this is 1. Otherwise, it is 0.

SERVER_PROTOCOL-The name and version of the information retrieval protocol relating to this request. This is normally HTTP/1.0.

SERVER_SOFTWARE-The name and version of the Web server under which the ISAPI extensionPI DLL program is running.

ALL_HTTP-All HTTP headers that were not already parsed into one of the previous variables. These variables are of the form HTTP_<header field name>. The headers consist of a null-terminated string with the individual headers separated by line feeds.

HTTP_ACCEPT-A special-case HTTP header. Values of the Accept: fields are concatenated and separated by a comma (","). For example, if the following lines are part of the HTTP header

accept: */*; q=0.1

accept: text/html

accept: image/jpeg

the HTTP_ACCEPT variable has a value of:

*/*; q=0.1, text/html, image/jpeg

URL-This gives the base portion of the URL.


[Note]


With respect to Auth_Type, if the string is not empty, it does not mean the user was authenticated if the authentication scheme is not "basic" or "NTLM." The server allows authentication schemes it does not understand because an ISAPI extensionPI filter may be able to handle that scheme.


The ReadClient function reads information from the body of the Web client's request into the buffer supplied by the caller. This allows the call to be used to read data from an HTML form that uses the POST method.

If more than lpdwSize bytes are immediately available to be read, ReadClient returns after transferring that amount of data into the buffer. Otherwise, it blocks and waits for data to become available.


[Note]


When a call to ReadClient is made and the socket on which the server is listening to the client is closed, ReadClient returns TRUE but with 0 bytes read.


The WriteClient function is used to send a formatted response to the HTTP client from the buffer supplied by the client. This function is also used to send binary data because it does not assume a zero-terminated string. The WriteClient function, unlike the ServerSupportFunction, function handles binary data.

The ServerSupportFunction provides the ISAPI extensions with general-purpose functions, as well as functions specific to the server.

Removing the Extension from Memory

ISAPI 2.0 provides extensions with an optional function that extensions can use to for control in properly shutting down and unloading the extension from memory. This function is called TerminateExtension (like GetExtensionVersion) and is called just before the server unloads the application.

This is a safe way to clean up threads and complete other shutdown type activities. The prototype is

BOOL WINAPI TerminateExtension( DWORD dwFlags );

#define HSE_TERM_ADVISORY_UNLOAD 0x00000001

#define HSE_TERM_MUST_UNLOAD 0x00000002

The dwFlags parameter is a bit field consisting of the following values:

HSE_TERM_ADVISORY_UNLOAD-The server wants to unload the extension. The extension can return TRUE if OK or FALSE if the server should not unload the extension.

HSE_TERM_MUST_UNLOAD-The server is indicating that the extension is about to be unloaded; the extension can't refuse.

ISAPI Extension Rules

ISAPI extensions are easy to create and can enhance the capabilities of any Web site. Following a few simple rules ensures that your extension works properly once deployed at the WEB site. These rules are as follows:

Flagging a Directory as Executable

ISAPI extensions are DLLs that are loaded dynamically by an server. For the extension to be loaded by a client application, the extension must reside in a virtual directory that has execute permissions assigned. If the directory does not have execute permissions, the extension cannot be loaded by the client application.


[Note]


If your extension starts other processes or uses other files to do its work, you must make sure that the extension is using an account with adequate permissions.


For example, if your extension uses other files, the account assigned to your program must have the correct permissions to use those files. The default account for extensions is the IUSR_computername account.


Using Extensions as 32-bit DLLs

ISAPI is a 32-bit interface (DLL) and the servers that work with ISAPI are also 32-bit. Since the extensions are loaded dynamically through defined entry points to the DLL, extensions must be used as 32-bit DLLs.

Avoiding 16-bit DLLs in Extensions

Avoid 16-bi DLLs in extensions. ISAPI extensions are 32-bit DLLs and should not call, link, or use any 16-bit code or DLLs to do their functions.

Windows NT is a 32-bit operating system, and servers that use ISAPI extensions are 32-bit processes. Use only 32-bit code or DLLs in your extensions.

Even if 16-bit DLLs could be called, the DLLs would impede system performance, they would need special code to map a 32-bit address space to a 16-bit address space, and they would not allow the Win32 synchronization objects to protect against multiple threads accessing the 16-bit code.

In short, do not use any 16-bit DLLs to for your ISAPI extensions.

Exposing Mandatory Entry Points

As explained earlier in this chapter, ISAPI extensions are used as DLLs. The extensions are also loaded by the server at runtime rather than at link time. The only way a DLL can be loaded at runtime by an executable is for the DLL to export or expose specified functions that the executable can get the address to and call.

If the ISAPI extensions allowed users to specify their own functions to expose, the server could not accommodate many (if any) users. For this reason, the ISAPI design specifies mandatory functions that the extension must use and accept so the server can use the extension. These mandatory functions are GetExtensionVersion and HttpExtensionProc.


[Caution]


A common mistake made when creating an ISAPI extension is not to expose the DLL entry points. Not exposing the entry points allows the functions to be called only from functions in the extension. The functions can be exported by either:

Extensions Must Be Thread-Safe

Since Windows NT is a multitasking operating system, the multiple events, such as page requests from an ISAPI extension, can occur at the same time. As a result, you must protect shared resources in the extension from simultaneous access.

For example, if your extension accesses databases as part of its function, you may need to protect it against simultaneous database access by multiple threads.

Windows NT offers a variety of thread synchronization objects that can be used by your extensions. The most common object is the critical section.

A critical section serializes resource access between multiple threads in a process. The critical section object lends itself to the extension architecture because the critical section is designed for synchronizing multiple threads in a single process.

A number of other thread synchronization objects are provided by the Win32 environment. These include mutexes, semaphores, and events.

You have to make your extensions thread-safe because you cannot count on the server that is using your extension. The server may process each request from a client in a separate thread.

Without proper thread synchronization, this kind of server use could crash your extension or return erroneous results to the client.

From Here...

In this chapter you learn how ISAPI extensions work. You learn how ISAPI extensions compare with CGI applications and why ISAPI extensions are a good alternative to CGI applications.

You also see how an ISAPI extension is called and used by servers and learn the rules for building durable extensions.


© 1996, QUE Corporation, an imprint of Macmillan Publishing USA, a Simon and Schuster Company.