A Sample ISAPI Filter -- ex35b.dll, ex35c.exe

It was hard to come up with a cute application for ISAPI filters. The one we thought up, ex35b.dll, is a useful visual logging utility. IIS, of course, logs all transactions to a file (or database), but you must stop the server before you can see the log file entries. With this example, you have a real-time transaction viewer that you can customize.

Choosing the Notification

Start by looking at the list of CHttpFilter virtual member functions on page 1050. Observe the calling sequence and the parameters. For the EX35B logging application, we chose OnReadRawData because it allowed full access to the incoming request and header text (from pRawData) and to the source and destination IP addresses (from pCtxt->GetServerVariable).

Sending Transaction Data to the Display Program

The ISAPI filter DLL can't display the transactions directly because it runs (as part of the IIS service process) on an invisible desktop. You need a separate program that displays text in a window, and you need a way to send data from the DLL to the display program. There are various ways to send the data across the process boundary. A conversation with Jeff Richter, the Windows guru who wrote Advanced Windows (Microsoft Press, 1997), led to the data being put in shared memory. Then a user-defined message, WM_SENDTEXT, is posted to the display program. These messages can queue up, so IIS can keep going independently of the display program.

We declared two handle data members in CEx35bFilter::m_hProcessDest and CEx35bFilter::m_hWndDest. We added code at the end of the GetFilterVersion function to set these data members to the display program's process ID and main window handle. The code finds the display program's main window by its title, ex35b, and then it gets the display program's process ID.

m_hProcessDest = NULL; if((m_hWndDest = ::FindWindow(NULL, "ex35b")) != NULL) {     DWORD dwProcessId;     GetWindowThreadProcessId(m_hWndDest, &dwProcessId);     m_hProcessDest = OpenProcess(PROCESS_DUP_HANDLE, FALSE, dwProcessId);     SendTextToWindow("EX35B filter started\r\n"); }

Below is a helper function, SendTextToWindow, which sends the WM_SENDTEXT message to the display program.

void CEx35bFilter::SendTextToWindow(char* pchData) {     if(m_hProcessDest != NULL) {         int nSize = strlen(pchData) + 1;         HANDLE hMMFReceiver;         HANDLE hMMF = ::CreateFileMapping((HANDLE) 0xFFFFFFFF, NULL,             PAGE_READWRITE, 0, nSize, NULL);         ASSERT(hMMF != NULL);         LPVOID lpvFile = ::MapViewOfFile(hMMF, FILE_MAP_WRITE, 0, 0, nSize);         ASSERT(lpvFile != NULL);         memcpy((char*) lpvFile, pchData, nSize);         ::DuplicateHandle(::GetCurrentProcess(), hMMF, m_hProcessDest,              &hMMFReceiver, 0, FALSE, DUPLICATE_SAME_ACCESS |              DUPLICATE_CLOSE_SOURCE);         ::PostMessage(m_hWndDest, WM_SENDTEXT, (WPARAM) 0,              (LPARAM) hMMFReceiver);         ::UnmapViewOfFile(lpvFile);     } }

The DuplicateHandle function makes a copy of EX35B's map handle, which it sends to the EX35C program in a message parameter. The EX35C process ID, determined in GetFilterVersion, is necessary for the DuplicateHandle call. Here is the filter's OnReadRawData function, which calls SendTextToWindow:

DWORD CEx35bFilter::OnReadRawData(CHttpFilterContext* pCtxt,     PHTTP_FILTER_RAW_DATA pRawData) {     TRACE ("CEx35bFilter::OnReadRawData\n");     // sends time/date, from IP, to IP, request data to a window     char pchVar[50] = "";     char pchOut[2000];     DWORD dwSize = 50;     BOOL bRet;     CString strGmt = CTime::GetCurrentTime().FormatGmt("%m/%d/%y %H:%M:%SGMT");     strcpy(pchOut, strGmt);     bRet = pCtxt->GetServerVariable("REMOTE_ADDR", pchVar, &dwSize);     if(bRet && dwSize > 1) {         strcat(pchOut, ", From ");         strcat(pchOut, pchVar);     }     bRet = pCtxt->GetServerVariable("SERVER_NAME", pchVar, &dwSize);     if(bRet && dwSize > 1) {         strcat(pchOut, ", To ");         strcat(pchOut, pchVar);     }      strcat(pchOut, "\r\n");     int nLength = strlen(pchOut);     // Raw data is not zero-terminated     strncat(pchOut, (const char*) pRawData->pvInData, pRawData->cbInData);     nLength += pRawData->cbInData;     pchOut[nLength] = `\0';     SendTextToWindow(pchOut);     return SF_STATUS_REQ_NEXT_NOTIFICATION; }

The Display Program

The display program, ex35c.exe, isn't very interesting. It's a standard AppWizard CRichEditView program with a WM_SENDTEXT handler in the main frame class:

LONG CMainFrame::OnSendText(UINT wParam, LONG lParam) {     TRACE("CMainFrame::OnSendText\n");     LPVOID lpvFile = ::MapViewOfFile((HANDLE) lParam, FILE_MAP_READ, 0, 0,         0);     GetActiveView()->SendMessage(EM_SETSEL, (WPARAM) 999999, 1000000);     GetActiveView()->SendMessage(EM_REPLACESEL, (WPARAM) 0,          (LPARAM) lpvFile);     ::UnmapViewOfFile(lpvFile);     ::CloseHandle((HANDLE) lParam);     return 0; }

This function just relays the text to the view.

The EX35C CMainFrame class overrides OnUpdateFrameTitle to eliminate the document name from the main window's title. This ensures that the DLL can find the EX35C window by name.

The view class maps the WM_RBUTTONDOWN message to implement a context menu for erasing the view text. Apparently rich edit view windows don't support the WM_CONTEXTMENU message.

Building and Testing the EX35B ISAPI Filter

Build both the EX35B and EX35C projects, and then start the EX35C program. To specify loading of your new filter DLL, you must manually update the Registry. Run the Regedit application, and then double-click on Filter DLLs in \HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\W3SVC\Parameters. Add the full pathname of the DLL separated from other DLL names with a comma.

There's one more thing to do. You must change the IIS mode to allow the service to interact with the EX35C display program. To do this, click on the Services icon in the Control Panel, double-click on World Wide Web Publishing Service, and then check Allow Service To Interact With Desktop. Finally, use Internet Service Manager to stop and restart the WWW service to load the filter DLL. When you use the browser to retrieve pages from the server, you should see output like this.

click to view at full size.

You can use the same steps for debugging an ISAPI filter that you used for an ISAPI server extension.



Programming Visual C++
Advanced 3ds max 5 Modeling & Animating
ISBN: 1572318570
EAN: 2147483647
Year: 1997
Pages: 331
Authors: Boris Kulagin

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net