Observing the User Experience: A Practitioners Guide to User Research

The stencil processor is the feature of ATL Server that allows content generation (usually an HTML response) based on the stencil SRF files. This mechanism allows separating the static part of the response from the dynamic content. For example, the HTML payload required to describe a table and the font and the background color of the cells is completely separated in the SRF files from the actual values inside those table cells . This separation offers the following benefits:

Now, these advantages, although particularly significant in the context of a Web application, can t be ignored in other applications either. Just consider the large class of desktop applications that generates reports or must, somehow, generate formatted output. printf or CString::Format are pretty powerful formatters, but you can t use them to render 1,000 records coming from a local database. This section shows how you can use the ATL Server stencil processor to generate some completely non-HTML output in a stand-alone application.

How the ATL Server Stencil Processor Works

First, although we re talking about the stencil processor, ATL Server actually provides two stencil processors. The MSDN documentation for SRF files (see ms-help://MS.VSCC/MS.MSDNVS/vccore/html/ vcconATLServerServerResponseFileDefined.htm ) distinguishes between stencil files and SRF files. There s a basic stencil processor that implements the basic processing code for stencil files, and there s an HTML stencil processor for SRF files.

The HTML stencil processor is tightly integrated with the HTTP ISAPI extension architecture of ATL Server. This section deals with the basic stencil processor, so we ll just say a few words on the HTML stencil processor and then we ll focus on the basic one. The HTML stencil processor inherits from the basic stencil processor and adds some functionality that s very useful in the context of generating HTML response. Among these extensions are

A good reason for enumerating the features in the preceding list is that they are not supported in the basic stencil generator. Of course, you could implement them in another processor inheriting from the basic one, following the model of the HTML stencil.

So, let s see what the basic stencil processor can do. It implements the basic mechanism of content generation:

The stencil processing mechanism is implemented in the CStencil class (defined in the atlstencil.h file). This class can take care of loading a stencil document from a file, from a resource, or from a string, and parsing the stencil. Provided with an object that contains replacement methods, it can also walk the parsed stencil document and call into the replacement methods .

To interoperate with the CStencil class, an object containing replacement methods has to implement the ITagReplacer interface. This interface is used in two moments of the stencil processing. First, some of its methods (such as FindReplacementOffset ) are called during parsing of the stencil to make sure that all the replacement elements have an associated replacement method. This is to ensure that the element (for example) has the OnName method associated with it. Then, during the actual rendering, the RenderReplacement method is called to actually invoke the replacement method.

It s easy to realize that by implementing ITagReplacer directly, you ll end up with a big switch block that returns a function address or calls a function after comparing an input string (the element name) with a set of hard-coded values (the replacement elements supported by the implementation).

To help the implementers, ATL Server provides the ITagReplacerImpl template class, which hides all the implementation details behind macros such as

BEGIN_REPLACEMENT_METHOD_MAP(Replacer) REPLACEMENT_METHOD_ENTRY("Name", OnName) END_REPLACEMENT_METHOD_MAP

This macro system is far more readable and maintainable .

So, instead of implementing the ITagReplacer interface, the developer of a class containing the replacement methods faces the far more reasonable challenge of inheriting from the ITagReplacerImpl < > base class and focuses on his or her replacement handlers, as follows :

class CMyReplacementMethodsHolder: public ITagReplacerImpl< CMyReplacementMethodsHolder>

Now you can use the CStencil class to both parse and render the stencils. Next , you ll look at what you have to do to use the CStencil processor outside the regular HTML rendering context.

The next question that arises is Where is the content rendered? If you look at the definition of the CStencil class, you notice the Render method which, as you may expect, will render the actual content. Render is defined as follows:

virtual HTTP_CODE Render(ITagReplacer *pReplacer, IWriteStream *pWriteStream, CStencilState* pState = NULL)

The first parameter is the ITagReplacer implementation, which contains the replacement methods, as we ve already discussed. The second parameter is an IWriteStream implementation, an interface defined as follows:

__interface IWriteStream { HRESULT WriteStream(LPCSTR szOut, int nLen, DWORD *pdwWritten); HRESULT FlushStream(); };

You can implement this interface to write in almost any media. You could write by using an EXTENSION_CONTROL_BLOCK function (in the ISAPI context), or you could write into a socket, a pipe, a file, or the standard output.

The last parameter of the Render function is a CStencilState structure pointer. CStencilState is used to provide state information useful in the ISAPI context. As we don t cover the ISAPI context here, and the parameter is defaulted to NULL , we ignore it in this section.

We use the same approach in this section as we did in the previous section, meaning we present a sequence of steps to be performed to support stencil rendering in a non-Web application and then demonstrate each step in the sample provided for this section.

To use the stencil processor outside of the HTTP context, an application should

  1. Provide an implementation of the IWriteStream interface that will render the content to the desired media.

  2. Provide an implementation of the ITagReplacer interface, most likely by deriving a class from the ITagReplacerImpl helper class, and put into this class the handling methods for the stencil s replacement tags.

  3. Instantiate a CStencil object.

  4. Parse/render the stencil.

In the next section we analyze these steps in the associated sample, StanaloneStencilProc .

Stencil Processor Sample

As we stated in the beginning of this chapter, we show you how to use the stencil in a completely non-HTML- related way. For this purpose, the sample is a wizard for authoring SOAP servers and clients . The application allows users to design a Web service through some (hopefully) clear and intuitive mouse actions, and then it generates the code for the SOAP server and for the client. It goes the extra mile in helping (or annoying) the developer by generating valid code and providing suggestions on how/where the SOAP parameters should be used, allocated, or freed.

The sample builds an in-memory list of all the functions of the Web service, each function containing the list of parameters. Then it uses this list to generate the code for the client and for the server. To make the code more readable, the Microsoft Foundation Classes (MFC) interface is almost completely separated from the actual file rendering code. The only files that are involved in rendering are ContentGenerator.h and SoapWizard.h.

The stencils used for rendering the server and client files are in the sample s Templates subfolder. The code for the actual server and client is rendered based on the files having one of the following extensions: .cpp.srf or .h.srf. The sample also generates a Visual Studio .NET solution file and two project files, but they might not be valid in upcoming versions of Visual Studio.

Let s start by looking at the stencil files in the Templates subfolder. The important code resides in TemplateClient\TemplateClient.cpp.srf and in TemplateServer\TemplateServer.srf. The other files aren t very important and don t contain many replacement tags.

Why Are the Stencil Replacement Tags So Poorly Indented?

The replacement tags indentation often looks weird or just plain ugly. For example, the following fragment

;

would be so much better looking and readable if it was indented like this:

{Param(Name)}};

Well, the reason for this indenting scheme is to ensure that the result code will be good looking. For a function like the following:

HRESULT DoSomething([in]int a, [out]int* pb, [in]int c)

the first stencil fragment will render this:

a; c;

while the second will render the following code (empty lines are intentional here):

a; c;

 
Note  

A block will render each and every character between these two replacement tags, including the new lines and white spaces. The same applies to an statement.

The main stencil processor code lies in the ContentGenerator.h file. It starts with the CWriteStreamOnFile class, an implementation of the IWriteStream interface that allows content rendering into a file using the C++ streams.

Besides the required WriteStream and Flush methods, it provides operator < < implementations for some very common types such as LPCTSTR , CString & , and unsigned int , which are widely used in the replacement handler implementation.

The second component is the CSAHandler class (the StandAlone replacement handler), which implements the ITagReplacer interface by inheriting from ITagReplacerImpl . The class holds a pointer to the stream to render to and contains a replacement map, a set of REPLACEMENT_METHOD_ENTRY macros that map internal methods to replacement tags that can occur inside a stencil.

In this particular implementation, CSAHandler also controls the CStencil object. It provides a single entry-point for rendering (the renderFile method) that executes steps 3 and 4 in the algorithm described previously for supporting stencil processing in non-Web applications.

The main code path (with error checking removed for clarity) for what renderFile does is as follows:

CStencil stencil; hRet = stencil.LoadFromFile(szSRFFile); bRet = stencil.ParseReplacements(this); stencil.FinishParseReplacements(); hRet = stencil.Render(this, pStream);

The function calls perform the following actions:

The last class in the file, CContentGenerator , does the management work. It creates the destination folder, instantiates a CSAHandler object, and calls renderFile for all the files that are part of the combined client/server solution.

After you execute the sample, you have to open and build the generated project in Visual Studio through these steps:

  1. Build the server project.

  2. Right-click the WSDL file in the client and select Update Web Reference. This is to run the sproxy tool and generate the proxy header file.

  3. Build and run the client application.

The servers (and clients) generated by this sample don t completely cover the SOAP support provided by ATL Server. The wizard doesn t support compound types inside the SOAP methods (structs and arrays), and it doesn t allow specifying SOAP headers. The reason for this limitation is to keep the stencils as simple as possible.

Категории