COUNTER: MFC Internet Server Extension DLL

Click to open or copy the COUNTER project files.

The COUNTER sample illustrates the development of an Internet server extension using MFC. This extension demonstrates how to use an Internet Server Application Programming Interface (ISAPI) DLL to send image data (rather than HTML data) back to a Web browser.

The COUNTER sample can be built to use MFC in a shared DLL or to link statically to MFC.

This sample requires a Web server that supports the ISAPI. For details, see "Installing the Sample" below.

Installing the Sample

After you build the sample, copy the DLL file that was created into a directory on your Web server. Make sure the directory offers users of your server EXECUTE privileges, and that you have installed the correct MFC DLLs on the server’s \<Windows>\SYSTEM directory.

Once the file has been installed, you can create HTML pages that will access the DLL as if it were an image file. The following HTML file demonstrates three ways of calling the counter. The example assumes that COUNTER has been installed into the /SCRIPTS virtual directory on your server.

<HTML>
<HEAD>
<TITLE>Counter Demonstration</TITLE>
</HEAD>
<BODY>
The current system time is:
<IMG SRC="/scripts/counter.dll?clock"><P>
Visits to the page referred to by /mydir/mypage.htm:
<IMG SRC="/scripts/counter.dll/mydir/mypage.htm"><P>
Visits to this page:
<IMG SRC="/scripts/counter.dll"><P>
</BODY>
</HTML>

If you have both READ and EXECUTE privileges on your /SCRIPTS directory, change the following lines as shown:

...
Visits to the page referred to by /mydir/mypage.htm:
<IMG SRC="/scripts/counter.dll/mydir/mypage.htm?"><P>
Visits to this page:
<IMG SRC="/scripts/counter.dll?"><P>
...

Running the Sample

To send back data from an ISAPI server in a format other than HTML, you will need to make a few minor changes. First, the StartContent and WriteTitle functions should not be called, since these output header information that only applies to HTML files. Second, AddHeader should be called with the proper content type string (see the beginning of Counter.cpp for an example). Finally, EndContent should not be called, since this function also only applies to HTML format output.

This sample sends back image data in XBM (x-bitmap) format. This format was chosen because the output is strictly text and, thus, the format is easy to implement. The XBM data is generated in the function OutputXBM from the image bits stored in Charset.h. The XBM format requires that the image bits be sent in reverse order. Therefore, the bits in Charset.h are reversed. For example, the digit "3" looks like the following:

0x3E 00111110
0x63 00110011
0x60 01100000
0x60 01100000
0x3C 00111101
0x60 01100000
0x60 01100000
0x60 01100000
0x63 01100011
0x3E 00111110

The XBM is a monochrome format. If you want color output, you will need to implement a different format, such as the GIF format, which is a binary format. To send back binary data, such as a GIF format image as output, you will need to complete some alternative steps. Since the CHtmlStream class does not allow streaming of individual bytes, you need to create a class derived from CHtmlStream, then use that class to send output to the client. Here is an example class:

class CBinaryHtmlStream : public CHtmlStream
{
public:
   // we have a special overload of << to allow for a single byte
   CBinaryHtmlStream& operator<<(BYTE b)
   {
      Write(&b, 1);
      return *this;
   }
}

To send data using this stream, replace the OutputXBM function with the following skeleton code:

static const TCHAR szContentType[] = _T("Content-Type: image/gif\r\n");

void CCounterExtension::OutputXBM(CHttpServerContext* pCtxt, CString& szDigits)
{
   // some code here which takes szDigits and creates a new image
   // which has the GIF representation of those digits. This
   // image will be stored in memory at m_ImageByteArray[].
   // The number of bytes in the image is stored in m_nNumberBytes.

   // Start by writing the proper content type to the client
   AddHeader(pCtxt, szContentType);

   CString strLength;
   strLength.Format("Content-Length: %d\r\n", m_nNumberBytes);
   AddHeader(pCtxt, (LPCTSTR)strLength);

   CBinaryHtmlStream* pStream = new CBinaryHtmlStream;
   ISAPIVERIFY(pStream != NULL);

   int nCount;
   for (nCount = 0; nCount<m_nNumberBytes; nCount++)
      *pStream << m_ImageByteArray[nCount];

   *pCtxt << *pStream;

   delete pStream;

   // ... more code here ...
}

This sample demonstrates the following keywords:

CHttpServer::GetExtensionVersion; ISAPIVERIFY; CHttpServer::AddHeader; CArchive; CCriticalSection