A Simple ISAPI Service


Rather than defining a new mechanism for exposing reusable information or code, the ATL Server team decided to use a standard COM interface, IServiceProvider , to allow access from the request handlers to the reusable pieces of code or data that reside in the ISAPI module. The IServiceProvider interface provides a way of obtaining a COM interface pointer (therefore, a callable object) without any other detailed knowledge about where or how the object is stored. This way, any object that exposes a COM interface can be contained in the ISAPI module and reused by the request handler, with the only requirement being that both the ISAPI and the consumer know about the interface exposed by that object.

The actual functionality of the service doesn t change the way it s exposed by the ISAPI module or the way it s used by the request handlers. This section covers how to implement and use a very simple service that only adds two integers and returns the result. The service functionality consists of a single function:

 HRESULT AddTwoNumbers( int inA, int inB, int* pOutRet); 

As specified previously, all the functionality of the service has to be packaged in a COM-like interface to be exposed by the ISAPI module. So, for this particular service, you ll create a new header file with the following content:

 #pragma once  // IGenericService.  __interface __declspec(uuid("5BC416A1-CBA5-4769-9DEB-75E673E229FF"))  IGenericService : public IUnknown  {      HRESULT AddTwoNumbers(int  inA, int  inB, int* pOutRet);  }; 

This header file contains the definition of the service. It has to be visible for both the ISAPI module (which will expose it) and the request handler (which will use it).

Note  

Please note that a service doesn t have to implement a newly declared interface. It can very well be a COM interface already implemented in some external component. For example, ATL Server uses a predefined interface ( ISAXXMLReader ), and its implementation that comes with MSXML, in exposing a SAX reader service.

As you just declared the interface (as opposed to using a predefined one), let s move to the implementation of this simple service. (For the purposes of this sample, the implementation will appear in the ISAPI module itself.)

 #include "..\GenericService\CommonServiceDefinition.h"  class  CGenericService : public IGenericService  {  public:          ULONG        STDMETHODCALLTYPE  AddRef()        {return 1;}          ULONG        STDMETHODCALLTYPE Release()        {return 1;}          HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, void** ppvObject)          {                  if( !ppvObject )                          return E_POINTER;                  if( IsEqualIID( riid, IID_IUnknown))                          *ppvObject = static_cast<IUnknown*>(this);                  else if( IsEqualIID( riid, __uuidof(IGenericService)))                          *ppvObject = static_cast<IGenericService*>(this);                  else                          return E_NOINTERFACE;                  AddRef();                  return S_OK;          }          HRESULT AddTwoNumbers(int inA, int inB, int* pOutRet)          {                  if( !pOutRet )                          return E_POINTER;                  *pOutRet  = inA + inB;                  return S_OK;          }  }; 

By now, you have a service implementation ( CGenericService ) and its interface ( IGenericService ). Let s see how this service can be exposed by the ISAPI extension module.

As mentioned previously, the CIsapiExtension class (the base class for all the ISAPI extension modules in ATL Server) implements the standard COM IServiceProvider interface. The IServiceProvider interface exposes the QueryService method defined as follows :

 virtual HRESULT STDMETHODCALLTYPE QueryService(          /* [in] */ REFGUID guidService,          /* [in] */ REFIID riid, /* [out] */ void ** ppvObject); 

The guidService parameter identifies the service to be returned, and riid describes the interface that s requested from that service. On success, ppvObject will contain the returned interface.

To expose the new service ( CGenericService ) in the ISAPI extension, you must override CIsapiExtension s QueryService method. This means that the default implementation for CIsapiExtension is no longer enough, so you should move from the code generated by the wizard, which looks like this:

 typedef CIsapiExtension<> ExtensionType;  ExtensionType   theExtension; 

to a new class, defined as follows:

 class CMyExtension : public CIsapiExtension<>  {  };  CMyExtension theExtension; 

The newly created class will contain a protected member, m_internalGenericSvc , of type CGenericService . This member is the actual service. All you have to do now is override the QueryService method:

 virtual HRESULT STDMETHODCALLTYPE QueryService(          REFGUID guidService, REFIID riid, void **ppvObject)  {  if( IsEqualIID(guidService, __uuidof(IGenericService))   &&       IsEqualIID(riid, __uuidof(IGenericService)) )        return m_internalGenericSvc.QueryInterface( riid, ppvObject);  else       return CIsapiExtension<>::QueryService( guidService, riid, ppvObject);  } 

You exploit here the fact that your service implements a single interface ( IGenericService ). You don t have to use a distinct GUID for the service, so you can use the same GUID of the interface.

Let s move now to consuming the service. In the request handler, you ll need an IServiceProvider pointer to query for your just-implemented service. Conveniently, a base class for all the request handlers ( IRequestHandlerT ) contains a pointer to the IServiceProvider interface implemented by the ISAPI module. That pointer is stored in the IRequestHandlerT::m_spServiceProvider member and can be used directly.

It s important that the service interface definition is visible in the request handler, so you ll include the definition header here (CommonServiceDefinition.h).

The code for retrieving the service from the ISAPI module and using it looks like the following (with error handling removed for clarity):

 m_HttpResponse   <<   "Result (10 + 20 ) = ";  CComPtr<IGenericService>   spGenSvc;  hRet = m_spServiceProvider->QueryService(                               __uuidof(IGenericService), &spGenSvc);  if( SUCCEEDED(hRet) )  {          hRet  =  spGenSvc->AddTwoNumbers(10, 20, &iRet);  }  m_HttpResponse  <<  iRet; 

When you place this code in a tag replacement method (say, in the OnHello method generated by the wizard), it will get the IGenericService pointer from the ISAPI module and then invoke AddTwoNumbers on the service and display the results.

So far, we ve explained how a service can be defined, exposed by the ISAPI module, and then reused from a request handler. The consumer part will remain the same for all the ISAPI services. In the following sections we discuss differences in how you can store services in the ISAPI module and the impact of these differences on service behavior.




ATL Server. High Performance C++ on. NET
Observing the User Experience: A Practitioners Guide to User Research
ISBN: B006Z372QQ
EAN: 2147483647
Year: 2002
Pages: 181

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