INFO: Tips for Debugging ISAPI DLLs
ID: Q152054
|
The information in this article applies to:
-
Microsoft Win32 Software Development Kit (SDK)
-
Microsoft Internet Information Server versions 1.0, 2.0
IMPORTANT: This article contains information about editing the registry.
Before you edit the registry, make sure you understand how to restore it if
a problem occurs. For information about how to do this, view the "Restoring
the Registry" Help topic in Regedit.exe or the "Restoring a Registry Key"
Help topic in Regedt32.exe.
SUMMARY
Writing an ISAPI extension or filter is no more difficult than writing any
other DLL, but debugging the DLL can be a challenge. This article provides
a few ideas for debugging an ISAPI DLL.
The following acronyms are used in this article:
ISAPI - Internet Server Application Programming Interface
WWW - World Wide Web
IIS - Internet Information Server
SDK - Software Development Kit
DLL - Dynamic Link Library
HTML - Hypertext Markup Language
The following approaches are discussed:
- Turning off DLL caching
- Using OutputDebugString in an ISAPI DLL
- Using MessageBox
- Writing output to a log file
- Running IIS interactively
- Using an alternate heap for memory
- Using CGIWRAP to debug without a server
MORE INFORMATION
ISAPI is a subset of the Win32 API. It allows WWW servers, such as
Microsoft's IIS, to be extended in two ways: extensions and filters. In an
ISAPI extension, a browser typically sends information entered in a form,
and the extension returns a complete HTML page built programmatically. In
an ISAPI filter, all browser data, both inbound and outbound, can be
modified before or after the server processes it. Extension and filter DLLs
must conform to a specification; there are specific functions that must be
implemented in order for the DLL to work.
Microsoft's IIS supports ISAPI on Windows NT Advanced Server versions 3.51
and 4.0. Microsoft's Peer Web Server supports ISAPI on Windows NT
Workstation 4.0 and Windows 95. This article focuses on IIS for Windows NT
Advanced Server, but many of the tips apply to all implementations of
ISAPI, even Web servers other than Microsoft's.
Tips for ISAPI Development
IIS runs as a Windows NT service in the local system account, which is what
makes debugging ISAPI DLLs difficult. Running as a service introduces
issues that are new to many programmers:
- The DLL runs in the local system context.
- There is no desktop.
- ISAPI extension procedures have an impersonated security context.
- ISAPI developers do not have access to the server source code so the
developers are extending an application they did not write.
The following tips are intended to aid in the development of ISAPI DLLs.
Turning Off DLL Caching
It is often desirable during development to force your ISAPI DLL to unload
after each request, allowing you to replace it with a new version. IIS, by
default, will hold the DLL in memory, keeping the DLL file in use. To
modify this default cache setting, use the registry editor to change the
following key:
HKEY_LOCAL_MACHINE
System
CurrentControlSet
Services
W3SVC
Parameters
WARNING: Using Registry Editor incorrectly can cause serious problems that
may require you to reinstall your operating system. Microsoft cannot
guarantee that problems resulting from the incorrect use of Registry Editor
can be solved. Use Registry Editor at your own risk.
For information about how to edit the registry, view the "Changing Keys And
Values" Help topic in Registry Editor (Regedit.exe) or the "Add and Delete
Information in the Registry" and "Edit Registry Data" Help topics in
Regedt32.exe. Note that you should back up the registry before you edit it.
If you are running Windows NT, you should also update your Emergency Repair
Disk (ERD).
Set CacheExtensions to 0 to disable caching, or set it to 1 to enable
caching. When caching is turned off, extra loading and unloading makes
ISAPI extensions dramatically slower, so use this option only for
development.
Filter DLLs are always loaded when the IIS service is running, and there is
no way to change filter loading behavior. You must stop the IIS service
with the administration tool, copy your new version over the old, and then
restart the WWW service. The administration tool can run on a remote
development machine controlling the server over the network.
OutputDebugString
An easy technique to debug either an ISAPI filter or extension is to use
OutputDebugString. This function is part of Win32 and it is a standard way
to send a string to a debug monitor. Use DBMON, included as a sample in the
Win32 SDK, to view debug strings. A fix is necessary to view ISAPI debug
output with DBMON, because the DLL runs in the local system account while
DBMON runs in the logged-in user account. Starting with the Win32 SDK for
Windows NT 4 beta 2, DBMON creates a NULL DACL and passes the security
attributes to both CreateEvent calls and the CreateFileMapping call. You
can modify DBMON yourself if you do not have the current SDK. The code
below shows how to make a NULL DACL:
SECURITY_ATTRIBUTES sa; SECURITY_DESCRIPTOR sd;
sa.nLength = sizeof(SECURITY_ATTRIBUTES);
sa.bInheritHandle = TRUE;
sa.lpSecurityDescriptor = &sd;
if(!InitializeSecurityDescriptor(&sd, SECURITY_DESCRIPTOR_REVISION))
return; // Handle errors
if(!SetSecurityDescriptorDacl(&sd, TRUE, (PACL)NULL, FALSE))
return; // Handle errors
// You may now pass &sa to CreateEvent and CreateFileMapping
MessageBox
The Online Help for MessageBox in the Win32 SDK describes a number of
rarely-used constants, including MB_SERVICE_NOTIFICATION and MB_TOPMOST. In
the context of ISAPI, add both of these flags to your message boxes because
without them, the message box call will fail. Your ISAPI DLL is running as
a service so it does not have a desktop, and these flags tell the operating
system to display the message on the logged-in user's desktop.
Please note that the value of MB_SERVICE_NOTIFICATION has changed on
Windows NT 4.0 because of a conflict with Windows 95. When you specify
MB_SERVICE_NOTIFICATION|MB_TOPMOST, your message will display properly on
all versions of Windows NT and Windows 95.
Do not use MessageBox for anything but debugging. If you need to record
error messages, use the Windows NT event log. See the Win32 sample
"logging" for information on how to use the event log. Depending on the SDK
version you have, the logging sample may be in the q_a subdirectory off the
root of the SDK CD, or it may be with other samples in \mstools\samples.
Output to a Log File
An easy way to track the flow of an ISAPI extension or filter is to use a
log file. You can use the normal Win32 file I/O functions such as
CreateFile, ReadFile, WriteFile, and CloseHandle. Your DLL's current
directory will be the system root (c:\winnt40\system32). Also, keep
security in mind because the default security context for your DLL is the
local system account. This applies even when impersonation is active. If
you do not allow the local system to create files in the appropriate
directory and do not specify a non-NULL security attribute, your CreateFile
call will fail.
The ISAPI homefilt sample in the SDK provides one example of a file-logging
mechanism.
Running IIS Interactively
A popular debugging option is to run IIS as a console application and use
Visual C++ or another debugger for full source debugging. Information to do
this is given in the SDK readme. Prior to Win32 SDK version 4 beta 2, the
readme was included in the ActiveX SDK from
http://www.microsoft.com/intdev. As of version 4 beta 2, ISAPI became part
of the Win32 SDK. Examine the Win32 SDK readme for the procedure to run IIS
as a console application.
Briefly, here are a few tips. IIS cannot run as a console application and a
service at the same time. You will have to stop all three IIS services,
WWW, FTP, and Gopher, before running IIS from a command prompt. Once IIS is
running, you can use PVIEW (part of Visual C++) or TLIST (part of the NT
resource kit) to get the process ID of INETINFO.EXE, and then use your
debugger to attach to the running process.
Another approach is to supply the full command line, inetinfo.exe -e W3SVC,
as described in the SDK readme, within the debug settings. For Visual C++,
choose the Build menu, select Settings, then select the Debug tab. Type the
full path name of "inetinfo.exe" as the Executable for Debug Session, and
type "-e W3SVC" in Program Arguments. See Visual C++ 4.1 tech note TN063:
Debugging Internet Extension DLLs, for details on using the Visual C++
debugger.
Running IIS as a console application still is not exactly the same as
running it as a service. For instance, authentication filters do not
function properly in the console version of IIS. Instead of debugging IIS
as a console application, it is possible to debug IIS running as a service.
Attach a debugger to the INETINFO.EXE process, then use a browser and
interact with your web site. When a debugger is attached to IIS, any
occurrence of DebugBreak() will stop the service and put the debugger in
full-source debugging mode. Each time you modify your DLL code, detach and
restart the service using the Microsoft Internet Service Manager tool.
The main cost of running IIS interactively is the start-up time required.
It takes a few seconds to start and stop your debugger and, during
development, this may become unacceptable. Be sure to consider the other
alternatives.
Using an Alternate Heap for Memory
An ISAPI DLL might share the process heap with the WWW service. ISAPI DLL
memory overwrites may not show up until later, and they may appear to be a
problem with IIS. When you suspect memory corruption problems, modify your
DLL code to use an alternate heap by calling HeapCreate, then HeapAlloc and
HeapFree for all memory allocations. If switching from the process heap to
a new heap changes the behavior of a crash, you can conclude that your DLL
is corrupting memory.
See the ISAPI sample CGIWRAP to isolate your extension DLL from the server.
CGIWRAP is an executable and will have its own address space so even when
your DLL corrupts memory, IIS will remain safe. Also, the CGIWRAP process
is terminated after each request is complete. This technique makes your
extension very slow but is a helpful interim solution until the corruption
bug is finally fixed.
The Win32 family of VirtualAlloc functions allows you to allocate 64K
blocks of memory and can modify the protection of a block of memory.
It can be helpful to use the memory protection provided by these functions
to diagnose memory overwrite bugs.
Finally, before your DLL is deployed in a production environment, check for
memory leaks. For example, cut and paste the following code to track the
number of bytes allocated. Call DumpBytesInUse() within your code to track
the number of bytes currently allocated:
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#if 1 // change to #if 0 to turn off mem check
DWORD g_dwTotal;
//
// Call MemAlloc just like you would call HeapAlloc
//
LPVOID MemAlloc (HANDLE hHeap, DWORD dwFlags, DWORD dwSize)
{
LPVOID p;
p = HeapAlloc (hHeap, dwFlags, dwSize);
if (p)
g_dwTotal += dwSize;
return p;
}
BOOL MemFree (HANDLE hHeap, DWORD dwFlags, LPVOID p)
{
DWORD dwSize;
BOOL bReturn;
dwSize = HeapSize (hHeap, dwFlags, p);
bReturn = HeapFree (hHeap, dwFlags, p);
if (dwSize != 0xffffffff && bReturn)
g_dwTotal -= dwSize;
return bReturn;
}
void DumpBytesInUse (void)
{
TCHAR szMsg[256];
wsprintf (szMsg, "Bytes in use: %u\r\n", g_dwTotal);
OutputDebugString (szMsg);
}
#else
#define MemAlloc HeapAlloc
#define MemFree HeapFree
#define DumpBytesInUse(x)
#endif
Using CGIWRAP to Debug Without a Server
An effective way to conduct basic testing on an ISAPI DLL is to use the
CGIWRAP sample. CGIWRAP is an executable that calls an ISAPI DLL. Although
it is intended to allow an ISAPI DLL run as a CGI executable, CGIWRAP can
be modified for use with a debugger.
There are two sources of input for an ISAPI DLL: HTTP header variables and
HTTP data. An ISAPI DLL retrieves header variables from GetServerVariable
and retrieves data from ReadClient. Similarly, a CGI executable gets its
header variables from the environment (with getenv) and gets its data from
standard input.
This means you can simulate a WWW server from the Command Line. Set
environment variables to match what is given to your ISAPI DLL and pipe a
file into CGIWRAP to simulate the inbound form data given to your DLL. When
CGIWRAP runs, it will translate environment variables and the piped input
into an ISAPI interface. Because you have the source to CGIWRAP, you can
set breakpoints on every line of code involved.
You can also improve the utility of CGIWRAP by modifying the sample. Add
your own Command Line options, including, perhaps, a "record" operation
when no Command Line options are specified. A record operation automates
the process of setting environment variables and piping standard input. The
idea is to run CGIWRAP as a normal CGI application from your calling web
page. CGIWRAP identifies that no Command Line parameters were sent, so it
records the environment and saves it to a file. Then, from a debugger (on
any machine), specify a Command Line option to CGIWRAP telling it to load
its environment from the previously saved file. CGIWRAP doesn't do this
now, but these changes are easy to make yourself.
Keep in mind that CGIWRAP is not exactly like IIS. For example, CGIWRAP
does not run in the context of a local system. This can make a tremendous
difference in the way your ISAPI DLL behaves. Fortunately, when your DLL
works with CGIWRAP but not with IIS, you'll know the problem is very likely
related to security.
Additional query words:
iis isapi cgiwrap ismoke debug www cgi activex cache
Keywords : kbtshoot kbnokeyword iisapi
Version : WINDOWS:; winnt:1.0,2.0
Platform : WINDOWS winnt
Issue type : kbinfo