home *** CD-ROM | disk | FTP | other *** search
/ Tricks of the Windows Gam…ming Gurus (2nd Edition) / Disc2.iso / msdn_vcb / samples / vc98 / mfc / internet / counter / counter.cpp < prev    next >
Encoding:
C/C++ Source or Header  |  1998-03-27  |  9.9 KB  |  359 lines

  1. /////////////////////////////////////////////////////////////////////////////
  2. // counter.cpp : Defines the initialization routines for the DLL.
  3. //
  4. // Written by Jeff Miller
  5. // of Microsoft Product Support Services, Languages Developer Support
  6. //
  7. // This is a part of the Microsoft Foundation Classes C++ library.
  8. // Copyright (C) 1992-1998 Microsoft Corporation
  9. // All rights reserved.
  10. //
  11. // This source code is only intended as a supplement to the
  12. // Microsoft Foundation Classes Reference and related
  13. // electronic documentation provided with the library.
  14. // See these sources for detailed information regarding the
  15. // Microsoft Foundation Classes product.
  16.  
  17. // This ISAPI DLL can be called in three ways:
  18. // <IMG SRC="/scripts/counter.dll?clock">
  19. // will return an x-bitmap image containing the current system time
  20. // <IMG SRC="/scripts/counter.dll/mydir/mypage.htm">
  21. // will return an x-bitmap image containing the accumulated
  22. // count for the identifier /mydir/mypage.htm
  23. // <IMG SRC="/scripts/counter.dll">
  24. // will return an x-bitmap image containing the accumulated
  25. // count for the page which called the DLL.
  26.  
  27. #include <afx.h>
  28. #include <afxdb.h>
  29. #include <afxisapi.h>
  30. #include "resource.h"
  31. #include "counter.h"
  32.  
  33. #include "charset.h"
  34.  
  35. // This program sends back xbitmap images rather than HTML.
  36. static const TCHAR szContentType[] = _T("Content-Type: image/x-xbitmap\r\n");
  37.  
  38. // The following defines the location and filename for the log file.
  39. // This should be placed in a location that can be read and written to.
  40. static const TCHAR szLogFile[] = _T("C:\\counter.log");
  41.  
  42.  
  43. // The following two headers will tell the client never to cache
  44. // this information, as it is dynamic.
  45. static const TCHAR szExpires[] = _T("Expires: Thu, 01 Jan 1995 01:00:00 GMT\r\n");
  46. static const TCHAR szNoCache[] = _T("Pragma: no-cache\r\n");
  47.  
  48. ///////////////////////////////////////////////////////////////////////
  49. // command-parsing map
  50.  
  51. BEGIN_PARSE_MAP(CCounterExtension, CHttpServer)
  52.     ON_PARSE_COMMAND(Clock, CCounterExtension, ITS_EMPTY)
  53.     ON_PARSE_COMMAND(Default, CCounterExtension, ITS_EMPTY)
  54.     DEFAULT_PARSE_COMMAND(Default, CCounterExtension)
  55. END_PARSE_MAP(CCounterExtension)
  56.  
  57.  
  58. ///////////////////////////////////////////////////////////////////////
  59. // The one and only CCounterExtension object
  60.  
  61. CCounterExtension theExtension;
  62.  
  63. ///////////////////////////////////////////////////////////////////////
  64. // CCounterExtension implementation
  65.  
  66. CCounterExtension::CCounterExtension()
  67. {
  68. }
  69.  
  70. CCounterExtension::~CCounterExtension()
  71. {
  72. }
  73.  
  74. BOOL CCounterExtension::GetExtensionVersion(HSE_VERSION_INFO* pVer)
  75. {
  76.     // Call default implementation for initialization
  77.     CHttpServer::GetExtensionVersion(pVer);
  78.  
  79.     // Load description string
  80.     TCHAR sz[HSE_MAX_EXT_DLL_NAME_LEN+1];
  81.     ISAPIVERIFY(::LoadString(AfxGetResourceHandle(),
  82.             IDS_SERVER, sz, HSE_MAX_EXT_DLL_NAME_LEN));
  83.     _tcscpy(pVer->lpszExtensionDesc, sz);
  84.     return TRUE;
  85. }
  86.  
  87. ///////////////////////////////////////////////////////////////////////
  88. // CCounterExtension command handlers
  89.  
  90. void CCounterExtension::Default(CHttpServerContext* pCtxt)
  91. {
  92.     // We don't call StartContent() or WriteTitle() here due to the
  93.     // fact that those will send back tags appropriate only to
  94.     // HTML pages.  We will be sending back an image.
  95.  
  96.     // Don't allow these pages to be cached
  97.     AddHeader(pCtxt, szExpires);
  98.     AddHeader(pCtxt, szNoCache);
  99.  
  100.     TCHAR pstrBuffer[1024];
  101.  
  102.     // Check to see if a path was given after the DLL's name,
  103.     // such as <IMG SRC="/scripts/counter.dll/mydir/mypage.htm">
  104.     // If so, use the given path.  Otherwise, use the URL of
  105.     // the page which called this DLL.
  106.  
  107.     if (_tcsclen(pCtxt->m_pECB->lpszPathInfo) != 0)
  108.     {
  109.         _tcscpy(pstrBuffer, pCtxt->m_pECB->lpszPathInfo);
  110.     }
  111.     else
  112.     {
  113.         // HTTP_REFERER contains the full URL of the page which
  114.         // called this DLL.
  115.         DWORD dwSize = 1024;
  116.         pCtxt->GetServerVariable(_T("HTTP_REFERER"), pstrBuffer, &dwSize);
  117.     }
  118.  
  119.     // Call member function to see how many times the requested
  120.     // page has been accessed and update that count.
  121.     // GetPageCount will return -1 if there was a problem reading the
  122.     // counter log file.
  123.  
  124.     CString szPath(pstrBuffer);
  125.     int nCount = GetPageCount(szPath);
  126.     if (nCount != -1)
  127.     {
  128.         // call member function to output an xbitmap image
  129.         // containing the digits
  130.         CString szCount;
  131.         szCount.Format(_T("%d"), nCount);
  132.         OutputXBM(pCtxt, szCount);
  133.     }
  134.  
  135.     // EndContent() is only appropriate for HTML files, so we
  136.     // don't call it here.
  137. }
  138.  
  139. void CCounterExtension::Clock(CHttpServerContext* pCtxt)
  140. {
  141.     // Clock
  142.     // Called when DLL is accessed using the format
  143.     // <IMG SRC="/scripts/counter.dll?Clock">
  144.  
  145.     // We don't call StartContent() or WriteTitle() here due to the
  146.     // fact that those will send back tags appropriate only to
  147.     // HTML pages.  We will be sending back an image.
  148.  
  149.     // Don't allow these pages to be cached
  150.     AddHeader(pCtxt, szExpires);
  151.     AddHeader(pCtxt, szNoCache);
  152.  
  153.     // Get the current system time, and put it into a form
  154.     // which our function will accept.
  155.     CTime time = CTime::GetCurrentTime();
  156.     CString szTime = time.Format(_T("%H%%%M"));
  157.  
  158.     // call member function to output an xbitmap image
  159.     // containing the digits
  160.     OutputXBM(pCtxt, szTime);
  161.  
  162.     // EndContent() is only appropriate for HTML files, so we
  163.     // don't call it here.
  164. }
  165.  
  166. void CCounterExtension::OutputXBM(CHttpServerContext* pCtxt, CString& szDigits)
  167. {
  168.     // Function to take in a string containing the digits 0..9 and the character
  169.     // ':' and output an xbitmap image of those digits to the stream
  170.  
  171.     // Start by writing the proper content type to the client
  172.     AddHeader(pCtxt, szContentType);
  173.  
  174.     // NOTE: this code as is will only work properly with image data
  175.     // that has a width = 8
  176.     int nFinalWidth = char_width * szDigits.GetLength();
  177.     int nFinalHeight = char_height;
  178.  
  179.     // write out the XBM header.  We cast to long int because there is
  180.     // no CHttpServerContext << operator overload that accepts an
  181.     // integer.
  182.     *pCtxt << _T("#define counter_width ") << (long int)nFinalWidth << _T("\r\n");
  183.     *pCtxt << _T("#define counter_height ") << (long int)nFinalHeight << _T("\r\n");
  184.     *pCtxt << _T("static unsigned char counter_bits[] = {\r\n");
  185.  
  186.     // Now for each horizontal line of output, get the bitmap for each
  187.     // character for that line and output it.
  188.     for (int nLine=0; nLine<nFinalHeight; nLine++)
  189.     {
  190.         for (int nChar=0; nChar<szDigits.GetLength(); nChar++)
  191.         {
  192.             int nDigitOffset;
  193.             if (szDigits[nChar] >= __TEXT('0') && szDigits[nChar] <= __TEXT('9'))
  194.                 nDigitOffset = szDigits[nChar] - __TEXT('0');
  195.             else
  196.                 // colon is in index 10 in the bitmap array
  197.                 nDigitOffset = 10;
  198.  
  199.             CString szHex;
  200.             szHex.Format(_T("0x%02X, "), char_bits[nDigitOffset][nLine]);
  201.             *pCtxt << szHex;
  202.         }
  203.     }
  204.  
  205.     *pCtxt << _T("};\r\n");
  206. }
  207.  
  208. int CCounterExtension::GetPageCount(CString& szPage)
  209. {
  210.     // Given a unique page identifier (szPage), check our "database"
  211.     // to see how many times this page has been accessed, then
  212.     // return that count.  If this function fails, -1 will be returned.
  213.  
  214.     CFile file;
  215.     CFileException e;
  216.     int nOpenAttempts = 0;
  217.  
  218.     // make sure only one thread operates on the log file at a time
  219.     while (TRUE)
  220.     {
  221.         if (file.Open(szLogFile,
  222.             CFile::modeRead | CFile::shareExclusive
  223.             | CFile::modeCreate | CFile::modeNoTruncate, &e))
  224.         {
  225.             // we opened the file, so continue onwards
  226.             break;
  227.         }
  228.         else
  229.         {
  230.             // we couldn't open the file...figure out why
  231.             if (e.m_cause == CFileException::sharingViolation)
  232.             {
  233.                 // sharing violation
  234.                 // another thread must have the file open, so wait and retry
  235.                 ::Sleep(100);
  236.  
  237.                 // increase the attempt counter
  238.                 nOpenAttempts++;
  239.                 if (nOpenAttempts == 30)
  240.                 {
  241.                     // too many retries. something's amiss
  242.                     CString szErrText;
  243.                     szErrText.LoadString(IDS_RETRYERR);
  244.                     TRACE0(szErrText);
  245.  
  246.                     // return error code
  247.                     return -1;
  248.                 }
  249.             }
  250.             else
  251.             {
  252.                 // not a sharing violation error
  253.                 // so it's probably serious
  254.                 TCHAR szCause[255];
  255.                 e.GetErrorMessage(szCause, 255);
  256.                 CString szErrText;
  257.                 szErrText.LoadString(IDS_OPENERR);
  258.                 TRACE1(szErrText, szCause);
  259.  
  260.                 // return error code
  261.                 return -1;
  262.             }
  263.         }
  264.     }
  265.  
  266.     // we've now successfully opened the log file so let's read it!
  267.  
  268.     CArchive archive(&file, CArchive::load);
  269.  
  270.     // Serialize in the data from the file
  271.     try
  272.     {
  273.         m_Paths.Serialize(archive);
  274.     }
  275.     catch (CArchiveException* e)
  276.     {
  277.         // if we get endOfFile, it probably means that the counter
  278.         // log file doesn't exist yet, so it's not a fatal error
  279.  
  280.         if (e->m_cause != CArchiveException::endOfFile)
  281.         {
  282.             TCHAR szCause[255];
  283.             e->GetErrorMessage(szCause, 255);
  284.             CString szErrText;
  285.             szErrText.LoadString(IDS_SERIALIZEERR);
  286.             TRACE1(szErrText, szCause);
  287.  
  288.             m_Paths.RemoveAll();
  289.  
  290.             archive.Close();
  291.             file.Close();
  292.             e->Delete();
  293.  
  294.             return -1;
  295.         }
  296.  
  297.         e->Delete();
  298.     }
  299.  
  300.     archive.Close();
  301.     file.Close();
  302.  
  303.     // Try to find the page identifier in the log
  304.     int nVal = 0;
  305.     CString szCount;
  306.     if (m_Paths.Lookup(szPage, szCount))
  307.         nVal = atoi(szCount);
  308.  
  309.     // Increment the count.
  310.     nVal++;
  311.  
  312.     // Set the new value.
  313.     szCount.Format(_T("%d"), nVal);
  314.     m_Paths.SetAt(szPage, szCount);
  315.  
  316.     // Write the updated log
  317.     if ( !file.Open(szLogFile,
  318.         CFile::modeWrite | CFile::shareExclusive
  319.         | CFile::modeCreate, &e) )
  320.     {
  321.         TCHAR szCause[255];
  322.         e.GetErrorMessage(szCause, 255);
  323.         CString szErrText;
  324.         szErrText.LoadString(IDS_OPENERR);
  325.         TRACE1(szErrText, szCause);
  326.  
  327.         // return error code
  328.         return -1;
  329.     }
  330.  
  331.     CArchive archiveStore(&file, CArchive::store);
  332.  
  333.     try
  334.     {
  335.         m_Paths.Serialize(archiveStore);
  336.     }
  337.     catch (CArchiveException* e)
  338.     {
  339.         TCHAR szCause[255];
  340.         e->GetErrorMessage(szCause, 255);
  341.         CString szErrText;
  342.         szErrText.LoadString(IDS_SERIALIZEERR);
  343.         TRACE1(szErrText, szCause);
  344.  
  345.         archiveStore.Close();
  346.         file.Close();
  347.         e->Delete();
  348.  
  349.         // return error code
  350.         return -1;
  351.     }
  352.  
  353.     archiveStore.Close();
  354.     file.Close();
  355.  
  356.     // Return the count we received
  357.     return nVal;
  358. }
  359.