ATL Server is a new set of classes in ATL for creating Web Applications and Web Services. These Web Services are completely compatible with clients written in C++, C#, VB and other tools that can call a Web Service through SOAP. The Web Service support in ATL Server only supports invocation over an HTTP transport for this version, however like the rest of ATL, ATL Server is designed to be very flexible. You may wish to create Web Services which can be called over other transport mechanisms, such as TCP/IP, and this article will help explain how you would go about changing the default transport ATL Server supports for Web Services. Two examples are included, one implementing a Web Service which can be called over TCP/IP and another sample which uses a Floppy Disk for transport.
This article shows how to separate the SOAP support offered by ATL Server from the ISAPI framework, allowing use of ATL Server to build SOAP servers and clients over virtually any communication channel.
The ATL Server Web Service Wizard will generate the code for a Web Service exposed through HTTP. The Web Service is implemented as a class with the following declaration:
class CSimpleSoapAppService :
In this code sequence, the attributes before the class declaration do most of the exposing. The
soap_handler attribute will inject the following methods:
__AtlGetSDL. These methods are required for required for handling SOAP messages and mapping them to internal method calls and also for wrapping the result of internal methods invocations to SOAP responses. (The MSDN library coming with Visual Studio .NET 7.0 Beta1 contains details on the code injected by this attribute). The
request_handler attribute will make it able to handle HTTP requests. If the transport layer is not going to be HTTP, the
request_handler attribute is no longer needed, so it can be commented out.
CSoapHandler<> class in atlsoap.h is designed to handle SOAP requests through HTTP. The entry point for handling an HTTP SOAP request is:
HTTP_CODE HandleRequest(AtlServerRequest *, IServiceProvider *)
The request body and the SOAP related HTTP header ("
SOAPAction") are retrieved from the ISAPI extension through the
pServerRequest (an ECB wrapper) member of the
AtlServerRequest* parameter. The
pServerRequest object provides methods from reading the request body, reading the
SOAPAction HTTP header and writing the SOAP response back to the client. As the purpose is to replace the HTTP transport, we need to find another way to launch the SOAP request processing and to provide it with a way of performing the same operations.
This is solved by creating a
CSoapTransportHandler, that implements the following entry point (file SoapTransportSrv.h of the downloadable code):
HTTP_CODE InvokeSoapMethod(stSoapTransportDescription *pTransInfo)
stSoapTransportDescription structure is defined as:
Perhaps the only member that needs a few details is
pServiceProvider. It allows the SOAP servers to share services exactly as those in an ISAPI DLL. It can be
NULL in an implementation, as long as the SOAP servers will not attempt to use it.
Now, the SOAP server class generated by the wizard has to be modified to use the new
CSoapTransportHandler functionality. The new code would look like:
class CSimpleSoapAppService :
There is no other modification that has to be done in the wizard-generated server code.
Right now, the SOAP server can be instantiated and the SOAP methods can be invoked through
CSoapTransportHandler::InvokeSoapMethod, therefore with custom streams to read from and write to, and it does not depend on the ATL Server HTTP support anymore.
The instantiation of the server(s) is handled by another class,
CSoapDispatcher, implemented in the soapDispatch.h file. The
CSoapDispatcher class fills a
stSoapTransportDescription structure, then instantiates a SOAP server and invokes a SOAP call like below:
HRESULT hRet = S_OK;
hRet = srv.InvokeSoapMethod( &transInfo );
At this point, a server application only has to:
- get the SOAP request and the SOAP Action header through any transport channel
- wrap the request in an
- provide an
IWriteStream to store the response
- send the content of the write stream (the SOAP response) over any transport channel to the client
The SOAP proxy class generated by sproxy.exe has the following prototype:
class CSimpleSoapAppService : public CSoapSocketClient
By inspecting the code generated by sproxy.exe, one can notice that only a few methods/members of the
CSoapSocketClient are actually used by the proxy class. These would be:
- the constructor
HRESULT SendRequest(LPCTSTR tszAction) throw()
void Cleanup() throw()
m_Stream, used as an
IStream interface to store the request before sending
m_ReadStream, used as an
IStream interface to store the response before parsing
The method that does most of the work here is
SendRequest. This method is supposed to send the request from
m_Stream to the SOAP server and to fill
m_ReadStream with the server response. By replacing (in the sproxy.exe generated file)
CSoapSocketClient with a custom class that contains all these methods and attributes, the actual channel used to send the request to the SOAP server only depends on the implementation of the
It is exactly what happens in the attached sample code. To reuse the sproxy.exe generated code for such different transport classes, the following modification would be useful:
class CSimpleSoapAppService : public TTransportClass
The sample implements the following transport channels:
- Communication through files saved on a storage media (in the FloppyTransport folder)
- TCP/IP communication (in the TCPIPTransport folder)
Each transport channel implementation contains a client application and a server application. The server uses the SOAP server included in the Include folder (which is built as described above). The code in the server application only takes care of the transport. The client application only implements the transport specific class to act as a
TTransportClass parameter for the modified class generated by sproxy.exe.
Please note that the modifications specified above only allow sending/receiving SOAP messages over the custom channel. They do not allow retrieving the description (SDL) for the service. Therefore, a limitation of the
FloppyTransport samples is that the SDL used in generating the proxy has to be generated outside of the sample. The one used in the applications specified above is saved as simpleSoapSrv.wsdl in the Include folder. To get the server description, one would have to create the SOAP server through the wizard, complete the interface, then save the SDL through the default HTTP way provided by ATL Server.
An Extension: HTTPListenerTransport
This folder contains an enhanced version of the
TCPIPTransport server. This server listens on a specified TCPIP port, but it accepts HTTP requests. It provides a way of responding to .disco requests, to generate the SDL for the implemented SOAP servers and also to accept SOAP invocations. This server can provide a very light-weight, complete, SOAP implementation, without requiring an HTTP server to be installed on the machine. To create a client for this light server, just use the Add Web Reference wizard from the Visual Studio.NET 7.0 Beta1 IDE, and point the wizard to the following URL:
If you build the
HTTPListenerServer sample, the URL would become:
The C# client provided for this server (
HttpListenerCSharpClient) is generated this way.
To build the sample code, just unzip the source archive, then load and build the solution file (ATLSSoapTransport.sln).
An updated version of the sample code will be included as an ATL Server sample starting with Visual Studio .NET Beta2. It will add the following:
This article has no explicit license attached to it, but may contain usage terms in the article text or the download files themselves. If in doubt, please contact the author via the discussion board below.
A list of licenses authors might use can be found here.