A CGI Web Server is an *.exe file - ISAPI and NSAPI Web Servers are *.dll files.
*.dll Web Servers are better (faster and more efficient) for high volume sites, but they are very difficult to debug because the main Web Server (IIS, Netscape, Apache, and the like) must be shut down each time a new *.dll is compiled. As a result, it is always easier to create a CGI server first and convert it to an *.dll latter. (Only the project (*.dpr) file is different.)
WebServer_CGI.html shows how to create a CGI server. The "only" difference between a CGI program and an ISAPI program is the *.dpr file. Other differences are discussed below.
Converting a CGI Application
Setting the output directory just makes testing a little easier.
Example *.dpr files
program WebCGI_frames; {$APPTYPE CONSOLE} uses WebBroker, CGIApp, WebCGI_Module_frames in 'WebCGI_Module_frames.pas' {WebModule1: TWebModule}; {$R *.RES} begin Application.Initialize; Application.CreateForm(TWebModule1, WebModule1); Application.Run; end.ISAPI *.drp file
library dll2; uses WebBroker, ISAPIApp, WebCGI_Module_frames in 'WebCGI_Module_frames.pas' {WebModule1: TWebModule}; {$R *.RES} exports GetExtensionVersion, HttpExtensionProc, TerminateExtension; begin Application.Initialize; Application.CreateForm(TWebModule1, WebModule1); Application.Run; end.Notice that these are identical except for
Running your program under IIS
Typically, your files are stored as
c:\Inetpub\wwwroot\xyz\WebCGI.dlland accessed as
http://localhost/xyz/WebCGI.dllNotice: You can not compile a new copy of your file if the current copy is open (running).
Testing
Basically, the web server (IIS in my case) opens the *.dll file and never closes it. Microsoft does not provide any mechanism for a dll to close itself - it must be closed by the program that opened it. Microsoft also does not provide a way for IIS to close individual *.dll's. As a result, you must shut down and restart IIS before your changes will take effect. (This is why I originally suggested developing your application as a CGI file and testing this first before you use it as a dll.)
The other problem is that the Delphi IDE debugger can not be used unless Delphi runs the code. Thus, when IIS runs your code, you will need to use other methods to debug it.
Other Differences
Filename and Command Line
To get the directory name in either an *.exe or an *.dll, use
var szFileName: array[0..MAX_PATH] of Char; DirStr : string; begin GetModuleFileName(hInstance, szFileName, MAX_PATH); DirStr := ExtractFilePath(szFileName);
It took several hours to find how to determine the directory that the *.dll file is in.
ExtractFilePath(Application.EXEName)is available only for applications that have a form - exename is implemented via
Result := ParamStr(0);in forms.pas, and ParamStr is defined in the system unit.
This is my test code
' + '' ;
Current Dir = ' + GetCurrentDir + '
Command line = ' + GetCommandLine + '
ParamStr(0) = ' + ParamStr(0) + '
Filename = ' + szFileName + '
Current Dir = c:\inetpub\wwwroot\rlc Command line = "c:\inetpub\wwwroot\rlc\Kill_CGI.exe" ParamStr(0) = c:\inetpub\wwwroot\rlc\Kill_CGI.exe Filename = c:\inetpub\wwwroot\rlc\Kill_CGI.exeIn an ISAPI dll file, it produces
Current Dir = C:\WINDOWS\SYSTEM32 Command line = C:\WINDOWS\System32\dllhost.exe /Processid:{3D14228D-FBE1-11D0-995D-00C04FD919C1} ParamStr(0) = C:\WINDOWS\System32\dllhost.exe Filename = c:\inetpub\wwwroot\rlc\Webdll_frames.dllFor dll's, CmdLine returns null.
I found the solution via Filename of the application (exe/dll) - it provides an example showing how to use the Windows GetModuleFileName command.
IsLibrary
if IsLibrary then
Timers
I have an ISAPI server that creates temporary jpeg's. Once every 10 minutes, I want the dll to check and erase any jpeg over 1 hour old. The logical choice was a TTimer - except that it was never called.
Instead, I added the cleanup code to the method that actually creates the jpeg files. Unfortunately, on a busy site, this code could run a thousand times a second instead of once every 10 minutes. I consider this to be a poor compromise.
Originally, I named all the jpegs the same - the application simply kept over writing the one file. However, some browsers cache the images and never show the correct data. I tried setting every html parameter I could find to force the browsers to dump the cache an load the current image. Since that never worked, my solution was to give each image a unique name and then to delete the old ones to save memory. Now I need to erase lots of temporary files.
Additional LInks
Debugging IIS5 ISAPI Applications with VC++ explains how to configure IIS and windows to allow debugging.
Author: Robert Clemenzi - clemenzi@cpcug.org