When writting an ISAPI module to be run "out-of-process", you must take care so that your ISAPI application is thread safe.
For example, let's take one of the examples in the ISAPI Introduction:
/* Import the ISAPI constants and type definitions */ #include "httpext.h" BOOL WINAPI GetExtensionVersion( HSE_VERSION_INFO *pVer ) { pVer->dwExtensionVersion = HSE_VERSION_MAJOR; strncpy(pVer->lpszExtensionDesc, "A simple page counter", HSE_MAX_EXT_DLL_NAME_LEN); return TRUE; } int hits=0; DWORD WINAPI HttpExtensionProc( LPEXTENSION_CONTROL_BLOCK ecb ) { char *header = "Content-Type: text/html"; int headerlen = strlen( header ); char msg[256]; int msglen; /* use a server support function to write out a header with our additional header information */ ecb->ServerSupportFunction( ecb->ConnID, HSE_REQ_SEND_RESPONSE_HEADER, 0, &headerlen, (DWORD *)header ); /* write out the number of accesses */ sprintf( msg, "This page has been accessed %d times", ++hits ); msglen = strlen( msg ); ecb->WriteClient( ecb->ConnID, msg, &msglen,0 ); /* return, indicating success */ return 0; }
as you can see in the code, this extension counts the number of times it was invoked using a global variable called hits. The problem with this architecture is that if it is running in a multithreaded environment, the global variable hits isn't thread safe, because more than one thread can access it at the same time.
To make it thread safe, we need to wrap this variable with a mutex variable, so that no more than one thread can access it at the same time:
/* Import the ISAPI constants and type definitions */ #include "httpext.h" #include <pthread.h> /* Assuming you are using POSIX threads. */ int hits=0; pthread_mutex_t hits_mutex; BOOL WINAPI GetExtensionVersion( HSE_VERSION_INFO *pVer ) { pVer->dwExtensionVersion = HSE_VERSION_MAJOR; strncpy(pVer->lpszExtensionDesc, "A simple page counter", HSE_MAX_EXT_DLL_NAME_LEN); /* Initialize the Mutex */ pthread_mutex_init( &hits_mutex, NULL ); return TRUE; } DWORD WINAPI HttpExtensionProc( LPEXTENSION_CONTROL_BLOCK ecb ) { char *header = "Content-Type: text/html"; int headerlen = strlen( header ); char msg[256]; int msglen; /* use a server support function to write out a header with our additional header information */ ecb->ServerSupportFunction( ecb->ConnID, HSE_REQ_SEND_RESPONSE_HEADER, 0, &headerlen, (DWORD *)header ); /* write out the number of accesses */ /* try to lock the mutex */ pthread_mutex_lock( &hits_mutex ); sprintf( msg, "This page has been accessed %d times", ++hits ); /* now unlock the mutex */ pthread_mutex_unlock( &hits_mutex ); msglen = strlen( msg ); ecb->WriteClient( ecb->ConnID, msg, &msglen,0 ); /* return, indicating success */ return 0; }
We defined
as a critical section of the code, by wrapping it with a mutex lock/unlock.sprintf( msg, "This page has been accessed %d times", ++hits );
You must also take care with what system library calls you use in your
application, as some parts of your systems libraries might not be trhead safe.
Please read the systems programming documentation of your operating system
if in doubt.
it will be called with the following values:void ZSLMain(int notification);
int notification | Meaning |
ZSLLOAD | The module was just loaded. Get*Version() will be called next. |
ZSLUNLOAD | Terminate() was called, and the module is about to be unloaded. |
ZSLTHREAD | A new thread was just created, or was just given this request to work in. Http*Proc() will be called next. |
ZSLEXIT | A thread just finished to do the work for this request. The thread will next exit, or wait for a new request. |
isapisnoop should only be used in a development environment.
Start isapisnoop, then restart your webserver: the zeus.isapi process will connect to the running isapisnoop and log the communication protocol. isapisnoop writes the log messages to stdout.
Use isapisnoop -d 2 to also log any ISAPI function calls made during the processing of a request.